Para mí, el camino a seguir sería interfaces y una fábrica. Uno que devuelve referencias a interfaces detrás de las cuales se pueden ocultar varias clases. Todas las clases que hacen el trabajo real deben estar registradas en Factory para que sepa qué clase instanciar dado un conjunto de parámetros.
Nota: en lugar de interfaces, también podría usar clases base abstractas, pero el inconveniente es que para los lenguajes de herencia única, lo limita a una sola clase 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');
El código está en sintaxis Delphi (Pascal) ya que ese es el lenguaje con el que estoy más familiarizado.
Después de que todas las clases de implementación se registren en la fábrica, debería poder solicitar una referencia de interfaz a una instancia de dicha clase. Por ejemplo:
Factory.GetReader('SomeFileName.xml');
Factory.GetWriter('SomeExportFileName.ppt');
Factory.GetRepresentation(rtTable, 'html');
debería devolver una referencia de IReader a una instancia de TXMLReader; una referencia de IWriter a una instancia de TPowerPointWriter y una referencia de representación IR a una instancia de THTMLTable.
Ahora todo lo que necesita hacer el motor de renderizado es unir todo:
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;
La interfaz IReader debe proporcionar métodos para leer los datos que necesitan los implementadores de IRepresentation para construir la representación de los datos. De manera similar, IRepresentation debería proporcionar métodos que los implementadores de IWriter necesitan para exportar la representación de datos al formato de archivo de exportación solicitado.
Asumiendo que los datos en sus archivos son de naturaleza tabular, IReader y sus interfaces de soporte podrían verse así:
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;
Iterar sobre una mesa sería una cuestión de
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;
Como las representaciones pueden ser imágenes, gráficos y de naturaleza textual, la representación IR probablemente tendría métodos similares a IReader para atravesar una tabla construida y tendría métodos para obtener las imágenes y gráficos, por ejemplo, como una secuencia de bytes. Correspondería a los implementadores de IWriter codificar los valores de la tabla y los bytes de imagen / gráfico según lo requiera el objetivo de exportación.