Sono un po 'intrappolato nel mio progetto. Devo estenderlo ben oltre le aspettative iniziali.
In primo luogo mostrerò come stanno le cose adesso e quali comportamenti voglio aggiungere ma non so come.
Attualmente, il seguente codice funziona correttamente. Alcune cose possono essere discusse (come la classe dei costruttori, che esiste per il Principio di Responsabilità Unica, ma forse non è il modo più intelligente per farlo, o la strana soluzione di covarianza del tipo di ritorno). Scusa per il muro di codice in arrivo, ho provato il più possibile per mostrarti il codice minimo per capire il problema.
public class QueryManager
{
// filled with different instances of Query in constructor depending on parameters
public List<Query> QueryList {get; set;}
public List<IRecords> RecordsList { get; set; }
public virtual void Queries()
{
foreach (var query in QueryList)
{
query.ExecutePreparedQuery();
var listResult = query.GetRecordsFromResults();
// i'm aware of the possibility of NullArgumentException here,
// i just simplify the actual thing
RecordsList.AddRange(listResult);
}
}
}
public abstract class Query
{
public FrameworkTypeHoldingResults Result { get; protected set; }
// Workaround for C# return type covariance.
protected virtual IRecordBuilder RecordBuilder
{
get { return GetRecordBuilder(); }
}
protected abstract IRecordBuilder GetRecordBuilder();
protected WebObject Web {get; set;}
public virtual void ExecutePreparedQuery()
{
var query = new FrameworkTypeQuery { ... }
Result = FrameworkType.GetItems(query)
}
protected virtual string DateFormatting() { ... }
public abstract IList<IRecords> GetRecordsFromResults();
public abstract string QueryDescription();
}
public class RealQuery : Query
{
protected override IRecordBuilder GetRecordBuilder()
{
return RecordBuilder;
}
protected new ConcreteRecordBuilderA RecordBuilder { get; set; }
public override string QueryDescription() => "I'm a RealQuery";
public override IList<IRecords> GetRecordsFromResults()
{
var recordsList = new List<IRecords>();
if (Result != null && Result.Count != 0)
{
RecordBuilder.Value = Result.Count;
RecordBuilder.Querytype = QueryDescription();
RecordBuilder.Dateformatted = DateFormatting();
RecordBuilder.WebTitle = Web.Title;
recordsList.Add(RecordBuilder.BuildRecord());
return recordsList;
}
RecordBuilder.Value = 0;
RecordBuilder.Querytype = QueryDescription();
RecordBuilder.Dateformatted = DateFormatting();
RecordBuilder.WebTitle = Web.Title;
recordsList.Add(RecordBuilder.BuildRecord());
return recordsList;
}
}
public interface IRecords{}
public interface IRecordBuilder
{
IRecords BuildRecord();
}
public class RecordsA
{
public RecordsA(int? Value, string QueryType, string Type, string Month) {...}
public int? Value {get; private set;}
public string QueryType {get; private set;}
public string Web {get; private set;}
public string Month {get; private set;}
}
public class RecordsB
{
public RecordsB(int? Value, string QueryType, string Type, string Month) {...}
public int? Value {get; private set;}
public string QueryType {get; private set;}
public string Web {get; private set;}
public string User {get; private set;}
}
public class RecordABuilder
{
public int? Value {get; set;}
public string QueryType {get; set;}
public string Web {get; set;}
public string Month {get; set;}
public IRecord BuildRecord() => new RecordA(Value, QueryType, Type, Month)
}
public class RecordBBuilder
{
// pretty much the same logic
}
Possiamo notare che sto solo utilizzando i metadati del risultato della mia query e non i risultati della query stessi.
Ora ecco il mio vero problema. Ho bisogno di un nuovo tipo di query. Il problema con questa query, a differenza del precedente, è che non conosco la natura degli IRecords che otterrò. Ti mostrerò dove interroga i suoi dati.
Web | QueryType | Value | Month | User
foo | B type | 42 | April 2020 |
bar | A type | 777 | | "John Doe"
Come vedi qui, quando il mese è pieno, l'utente non è (ans il contrario). Se la query ottiene la prima riga, avrò bisogno di un'istanza RecordA, ma se la query ottiene il secondo, avrò bisogno di un'istanza RecordB. La query, questa volta, utilizza direttamente i risultati della query e la classe non sa cosa otterrà. Il problema con if-condition è che se vengono aggiunte più colonne e più IRecords, se sarà grande un albero gigante di if-condition.
Finora, questa è l'implementazione GetRecordsFromResults che ho, ma funziona solo con un'implementazione di IRecords, non con entrambi:
public override IList<IRecords> GetRecordsFromResults()
{
var recordsList = new List<IRecords>();
if (Result != null && Result.Count != 0)
{
for (var index = 0; index < Result.Count; index++)
{
var it = Result[index];
RecordBuilder.Value = int.Parse(it["Value"].ToString());
RecordBuilder.Querytype = it["QueryType"] as string;
// But maybe i want the User column instead...
RecordBuilder.Month= it["Month"] as string;
RecordBuilder.WebTitle = it["Web"] as string;
recordsList.Add(RecordBuilder.BuildRecord());
// would a RecordsBBuilder in some cases
RecordBuilder = new RecordsABuilder();
}
return recordsList;
}
RecordBuilder.Value = 0;
RecordBuilder.Querytype = QueryDescription();
// But maybe i want the User column instead...
RecordBuilder.Month= DateFormatting();
RecordBuilder.Web = Web.Title;
recordsList.Add(RecordBuilder.BuildRecord());
// would a RecordsBBuilder in some cases
RecordBuilder = new RecordsPerMonthBuilder();
return recordsList;
}
Sono un po 'bloccato qui, hai qualche idea su come posso implementare questo nuovo tipo di Query concreto?
EDIT: non ho chiarito che la relazione tra Query
implementazione e IRecords
non è una per una. RecordA è per una famiglia di implementazioni Query
(identificate da una sottoclasse astratta tra Query
e le sue implementazioni) e RecordB per implementazioni dirette (senza sottoclassi intermedie). Potrebbero esserci anche RecordC in futuro. Ogni implementazione di Query
ha il proprio metodo di descrizione che viene utilizzato per riempire la proprietà QueryType
in IRecords
implementazioni.