Para mim, o caminho a seguir seria interfaces e uma fábrica. Um que retorna referências a interfaces por trás das quais várias classes podem se esconder. Todas as classes que funcionam com o grunhido real precisam ser registradas na fábrica para saber qual classe instanciar, com base em um conjunto de parâmetros.
Nota: em vez de interfaces, você também pode usar classes base abstratas, mas a desvantagem é que, para linguagens de herança únicas, limita-o a uma única 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');
O código está na sintaxe Delphi (Pascal), pois é o idioma com o qual estou mais familiarizado.
Depois que todas as classes de implementação forem registradas na fábrica, você poderá solicitar uma referência de interface para uma instância dessa classe. Por exemplo:
Factory.GetReader('SomeFileName.xml');
Factory.GetWriter('SomeExportFileName.ppt');
Factory.GetRepresentation(rtTable, 'html');
deve retornar uma referência IReader para uma instância de TXMLReader; uma referência IWriter para uma instância do TPowerPointWriter e uma referência IRepresentation para uma instância do THTMLTable.
Agora, tudo o que o mecanismo de renderização precisa fazer é unir tudo:
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;
A interface IReader deve fornecer métodos para ler os dados necessários aos implementadores de IRepresentation para construir a representação dos dados. Da mesma forma, a IRepresentation deve fornecer métodos que os implementadores de IWriter precisam para exportar a representação de dados para o formato de arquivo de exportação solicitado.
Supondo que os dados em seus arquivos sejam de natureza tabular, o IReader e suas interfaces de suporte podem ter a seguinte aparência:
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 uma mesa seria uma questão 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 as representações podem ter imagens, gráficos e natureza textual, a IRepresentation provavelmente teria métodos semelhantes ao IReader para percorrer uma tabela construída e teria métodos para obter as imagens e gráficos, por exemplo, como um fluxo de bytes. Caberia aos implementadores do IWriter codificar os valores da tabela e os bytes de imagem / gráfico, conforme exigido pelo destino de exportação.