Для мене шляхом пройти були б інтерфейси та Фабрика. Той, який повертає посилання на інтерфейси, за якими можуть ховатися різні класи. Усі класи, які виконують фактичну грунтовну роботу, повинні бути зареєстровані на Фабриці, щоб вона знала, до якого класу призначати заданий набір параметрів.
Примітка: замість інтерфейсів ви також можете використовувати абстрактні базові класи, але недоліком є те, що для окремих мов успадкування він обмежує вас одним базовим класом.
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');
Код є в синтаксисі Delphi (Pascal), оскільки це мова, якою я найбільше знайомий.
Після того, як всі класи реалізації зареєстровані на заводі, ви повинні мати можливість запитувати посилання на інтерфейс до примірника такого класу. Наприклад:
Factory.GetReader('SomeFileName.xml');
Factory.GetWriter('SomeExportFileName.ppt');
Factory.GetRepresentation(rtTable, 'html');
повинен повернути посилання IReader на екземпляр TXMLReader; посилання IWriter на екземпляр TPowerPointWriter та посилання IRepresentation на екземпляр THTMLTable.
Тепер все, що потрібно зробити, - це зв'язати все разом:
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;
Інтерфейс IReader повинен забезпечувати методи зчитування даних, необхідних реалізаторам IRepresentation для побудови представлення даних. Аналогічно IRepresentation має забезпечувати методи, необхідні реалізаторам IWriter для експорту представлення даних у потрібний формат файлу експорту.
Якщо припустити, що дані у ваших файлах мають табличну природу, IReader та його підтримуючі інтерфейси можуть виглядати так:
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;
Ітерація над столом тоді була б справою
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;
Оскільки представлення можуть мати зображення, графіки та текстові за своєю суттю, IRepresentation, ймовірно, матиме подібні методи, ніж IReader для обходу побудованої таблиці, і у неї будуть методи отримання зображень та графіків, наприклад у вигляді потоку байтів. Реалізатори IWriter повинні кодувати значення таблиці та байти зображень / графіків, як цього вимагає ціль експорту.