Per me, la strada da percorrere sarebbe un'interfaccia e una fabbrica. Uno che restituisce riferimenti alle interfacce dietro cui possono nascondersi varie classi. Le classi che eseguono il lavoro effettivo devono essere registrate con la Factory in modo che sappia quale classe istanziare in base a una serie di parametri.
Nota: al posto delle interfacce è possibile utilizzare anche classi astratte di base, ma lo svantaggio è che per i linguaggi di ereditarietà singola si limita a una singola classe 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 è in sintassi Delphi (Pascal) in quanto è la lingua con cui sono più familiare.
Dopo che tutte le classi di implementazione sono state registrate in fabbrica, dovresti essere in grado di richiedere un riferimento all'interfaccia per un'istanza di tale classe. Ad 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 a IRepresentation a un'istanza di THTMLTable.
Ora tutto ciò che deve fare il motore di rendering è 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 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 metodi che gli implementatori di IWriter devono 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 avere il seguente aspetto:
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;
L'iterazione 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 probabilmente avrebbe metodi simili per IReader per attraversare una tabella costruita e avrebbe metodi per ottenere immagini e grafici, ad esempio come un flusso di byte. Sarebbe compito degli implementatori di IWriter codificare i valori della tabella e i byte immagine / grafico come richiesto dalla destinazione di esportazione.