Per me, la strada da percorrere sarebbe interfacce e una fabbrica. Uno che restituisce riferimenti a interfacce dietro le quali possono nascondersi varie classi. Le classi che eseguono il vero grugnito devono essere tutte registrate con Factory in modo che sappia quale classe creare un'istanza dato un set di parametri.
Nota: al posto delle interfacce è possibile utilizzare anche classi di base astratte, ma lo svantaggio è che per i singoli linguaggi di ereditarietà si limita a una singola classe di base.
TRepresentationType = (rtImage, rtTable, rtGraph, ...);
Factory.RegisterReader(TJSONReader, 'json');
Factory.RegisterReader(TXMLReader, 'xml');
Factory.RegisterWriter(TPDFWriter, 'pdf');
Factory.RegisterWriter(TPowerPointWriter, 'ppt');
Factory.RegisterWriter(TWordWriter, 'doc');
Factory.RegisterWriter(TWordWriter, 'docx');
Factory.RegisterRepresentation(TPNGImage, rtImage, 'png');
Factory.RegisterRepresentation(TGIFImage, rtImage, 'gif');
Factory.RegisterRepresentation(TJPGImage, rtImage, 'jpg');
Factory.RegisterRepresentation(TCsvTable, rtTable, 'csv');
Factory.RegisterRepresentation(THTMLTable, rtTable, 'html');
Factory.RegisterRepresentation(TBarChart, rtTGraph, 'bar');
Factory.RegisterRepresentation(TPieChart, rtTGraph, 'pie');
Il codice è nella sintassi di Delfi (Pascal) in quanto quella è la lingua con cui ho più familiarità.
Dopo che tutte le classi di implementazione sono state registrate in fabbrica, si dovrebbe essere in grado di richiedere un riferimento all'interfaccia a un'istanza di tale classe. Per esempio:
Factory.GetReader('SomeFileName.xml');
Factory.GetWriter('SomeExportFileName.ppt');
Factory.GetRepresentation(rtTable, 'html');
dovrebbe restituire un riferimento IReader a un'istanza di TXMLReader; un riferimento IWriter a un'istanza di TPowerPointWriter e un riferimento IRepresentation a un'istanza di THTMLTable.
Ora tutto ciò che il motore di rendering deve fare è legare tutto insieme:
procedure Render(
aDataFile: string;
aExportFile: string;
aRepresentationType: TRepresentationType;
aFormat: string;
);
var
Reader: IReader;
Writer: IWriter;
Representation: IRepresentation;
begin
Reader := Factory.GetReaderFor(aDataFile);
Writer := Factory.GetWriterFor(aExportFile);
Representation := Factory.GetRepresentationFor(aRepresentationType, aFormat);
Representation.ConstructFrom(Reader);
Writer.SaveToFile(Representation);
end;
L'interfaccia di IReader dovrebbe fornire metodi per leggere i dati necessari agli implementatori di IRepresentation per costruire la rappresentazione dei dati. Allo stesso modo IRepresentation dovrebbe fornire i metodi di cui hanno bisogno gli implementatori di IWriter per esportare la rappresentazione dei dati nel formato di file di esportazione richiesto.
Supponendo che i dati nei tuoi file siano di natura tabellare, IReader e le sue interfacce di supporto potrebbero apparire come:
IReader = interface(IInterface)
function MoveNext: Boolean;
function GetCurrent: IRow;
end;
IRow = interface(IInterface)
function MoveNext: Boolean;
function GetCurrent: ICol;
end;
ICol = interface(IInterface)
function GetName: string;
function GetValue: Variant;
end;
Iterare su un tavolo sarebbe quindi una questione di
while Reader.MoveNext do
begin
Row := Reader.GetCurrent;
while Row.MoveNext do
begin
Col := Row.GetCurrent;
// Do something with the column's name or value
end;
end;
Poiché le rappresentazioni possono essere immagini, grafici e di natura testuale, IRepresentation avrebbe probabilmente metodi simili a IReader per attraversare una tabella costruita e avrebbe metodi per ottenere immagini e grafici, ad esempio come un flusso di byte. Spetterebbe agli implementatori di IWriter codificare i valori della tabella e i byte immagine / grafico come richiesto dalla destinazione di esportazione.