Pour moi, la voie à suivre serait des interfaces et une usine. Celui qui renvoie des références aux interfaces derrière lesquelles diverses classes peuvent se cacher. Les classes qui effectuent le travail de grognement réel doivent toutes être enregistrées auprès de la fabrique afin qu'elle sache quelle classe instancier en fonction d'un ensemble de paramètres.
Remarque: au lieu d'interfaces, vous pouvez également utiliser des classes de base abstraites, mais l'inconvénient est que pour les langages à héritage unique, il vous limite à une seule classe de 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');
Le code est en syntaxe Delphi (Pascal) car c'est le langage que je connais le mieux.
Une fois que toutes les classes d'implémentation sont enregistrées auprès de la fabrique, vous devriez pouvoir demander une référence d'interface à une instance d'une telle classe. Par exemple:
Factory.GetReader('SomeFileName.xml');
Factory.GetWriter('SomeExportFileName.ppt');
Factory.GetRepresentation(rtTable, 'html');
doit renvoyer une référence IReader à une instance de TXMLReader; une référence IWriter à une instance de TPowerPointWriter et une référence IRepresentation à une instance de THTMLTable.
Maintenant, tout ce que le moteur de rendu doit faire, c'est tout lier:
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'interface IReader devrait fournir des méthodes pour lire les données nécessaires aux implémenteurs IRepresentation pour construire la représentation des données. De même, IRepresentation doit fournir les méthodes dont les implémenteurs IWriter ont besoin pour exporter la représentation des données au format de fichier d'exportation demandé.
En supposant que les données de vos fichiers sont de nature tabulaire, IReader et ses interfaces de support pourraient ressembler à:
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;
Itérer sur une table serait alors une question 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;
Comme les représentations peuvent être des images, des graphiques et des textes, les représentations IR auraient probablement des méthodes similaires à IReader pour parcourir une table construite et des méthodes pour obtenir les images et les graphiques, par exemple sous la forme d'un flux d'octets. Il appartiendrait aux implémenteurs IWriter d'encoder les valeurs de la table et les octets image / graphique requis par la cible d'exportation.