Розробка надійної архітектури для декількох типів експорту?


10

Я шукаю зразки або архітектурні вказівки для майбутньої функції, яку я проектую. В основному, це експортна функція з кількома цілями експорту, і я шукаю спосіб зробити її загальною, коли підключення нових експортних цілей не потребує великих змін. Під експортними цілями я просто маю на увазі різні види вихідних даних, будь то PDF-файли, презентації PowerPoint, документи Word, RSS тощо. У мене є базовий набір даних, який представлений в JSON та XML. Ці дані використовуються для побудови зображень (з використанням будь-яких типів чи типів експорту [наприклад, PNG, JPG, GIF тощо), графіків, текстових зображень, таблиць тощо.

Я намагаюся знайти спосіб абстрагувати все візуалізацію та макет у якомусь механізмі візуалізації чи компонування, який обробляє додавання подальших цілей експорту. Будемо дуже вдячні за будь-яку допомогу / пропозиції / ресурси щодо того, як підходити до цього. Заздалегідь спасибі.

Для мальовничого зображення того, що я намагаюся досягти.

введіть тут опис зображення


Чи можете ви описати те, що ви пробували досі? Які вимоги (обов'язки) двигуна компонування? Наприклад, чи очікується обробка сторінки та вибору розміру сторінки?
rwong

Чи можуть дані XML / JSON використовуватися для створення декількох типів виводу на одному запуску, тобто ваші XML дані створюють зображення та таблиці та графіки у документі PDF? Чи можуть дані XML / JSON використовуватися лише для створення таблиці або діаграми для документа PDF?
Гібсон

Це все про xkcd.com/927 - чому ви намагаєтесь винаходити колесо? DocBook, Markdown / pandoc тощо вже існують ...
Мисливець на оленів,

Відповіді:


2

Для мене шляхом пройти були б інтерфейси та Фабрика. Той, який повертає посилання на інтерфейси, за якими можуть ховатися різні класи. Усі класи, які виконують фактичну грунтовну роботу, повинні бути зареєстровані на Фабриці, щоб вона знала, до якого класу призначати заданий набір параметрів.

Примітка: замість інтерфейсів ви також можете використовувати абстрактні базові класи, але недоліком є ​​те, що для окремих мов успадкування він обмежує вас одним базовим класом.

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 повинні кодувати значення таблиці та байти зображень / графіків, як цього вимагає ціль експорту.


1

Хоча я погоджуюся, що для роздуму архітектури потрібно більше інформації, найпростішим способом створення різних видів об'єктів, які поводяться однаково (тобто всі вони будуть генерувати вихід), є використання заводського зразка. Більше інформації тут

Заводський метод шаблону - це об'єктно-орієнтована модель креативного дизайну для реалізації концепції фабрик і займається проблемою створення об'єктів (продуктів) без уточнення точного класу об'єкта, який буде створений. Суть цієї шаблону полягає в тому, щоб "Визначити інтерфейс для створення об'єкта, але нехай класи, які реалізують інтерфейс, вирішують, який клас інстанціювати. Фабричний метод дозволяє відкладати ідентифікацію класу для підкласів." З вікіпедії


1
Я думаю, що це трохи більше, ніж це. Наприклад, які протоколи будуть використовуватися для передачі даних по рядках на діаграмі? Чи може бути загальне представлення даних у двигуні візуалізації / компонування, чи це двигун лише фабрика для повністю користувальницьких методів, по одному для кожного рядка діаграми?
Роберт Харві

Не впевнений, якщо я зрозумію тут. Тому що якщо потрібно використовувати протокол для передачі рядків на діаграмі, я думаю, що я покладаюся на набір послуг для генерації експорту (у цьому випадку вам сподобається побачити деякі схеми soa / інтеграції). Навіть якщо це правда, рішення є досить гнучким та надійним, щоб використовувати заводські. Можливо, те, що ви хочете зробити, - це створити інтерфейс перетворювача, який має два способи: один, який отримує XML-дані та інший для даних JSON. Об'єктом повернення для обох буде перетворений об’єкт. Таким чином ви зможете зібрати все, що завгодно.
Орпоузер

насправді у заголовку є два питання: про вміст (gif, pdf, html) та про транспорт (локальний файл, http-відповідь-пункт). Для розширення відповіді @Orposuser (+1): Я створив би потік, використовуючи фабрику, яку можна легко перевірити і легко подати для http-відповіді.
k3b

0

Ви можете закінчити щось подібне.

Дві фабрики базуються навколо:

1 - для перетворення типу введення (Json / XML) в конкретну реалізацію способів перетворення цих даних у зображення / графік

2 - Друга фабрика для вирішення способу надання виводу на слово Document / PDF Document

Поліморфізм використовує загальний інтерфейс для всіх наданих даних. Таким чином зображення / таблицю можна переміщувати як простий інтерфейс.

1 - Фабрика для перетворення даних JSON / XML в конкретну реалізацію:

public enum DataTypeToConvertTo
{
    Image,
    Table,
    Graph,
    OtherData
}

public interface IDataConverter
{
    IConvertedData ConvertJsonDataToOutput(Json jsonData);
    IConvertedData ConvertXmlDataToOutput(XDocument xmlData);
}

public abstract class DataConverter : IDataConverter
{
    public DataConverter()
    {

    }

    public abstract IConvertedData ConvertDataToOutput(string data);
}

Фабрика нижче дозволяє перетворити дані xml Data або Json у потрібний конкретний тип.

public class DataConverterFactory
{
    public static IDataConverter GetDataConverter(DataTypeToConvertTo dataType)
    {
        switch(dataType)
        {
            case DataTypeToConvertTo.Image:
                return new ImageDataConverter();
            case DataTypeToConvertTo.Table:
                return new TableDataConverter();
            case DataTypeToConvertTo.OtherData:
                return new OtherDataConverter();
            default:
                throw new Exception("Unknown DataTypeToConvertTo");
        }
    }
}

Конкретні реалізації виконують всю важку роботу перетворених даних у відповідний тип. Вони також перетворюють дані в інтерфейс IConvertedData, який використовується для поліморфізму.

public sealed class ImageDataConverter : DataConverter
{
    public ImageDataConverter()
        : base()
    {

    }

    public override IConvertedData ConvertJsonDataToOutput(Json jsonData)
    {
        var convertedData = new ImageConvertedData();
        //Logic to convert to necessary datatype

        return convertedData;
    }

    public override IConvertedData ConvertXmlDataToOutput(XDocument xmlData)
    {
        var convertedData = new ImageConvertedData();
        //Logic to convert to necessary datatype

        return convertedData;
    }
}

public sealed class TableDataConverter : DataConverter
{
    public TableDataConverter()
        : base()
    {

    }

    public override IConvertedData ConvertJsonDataToOutput(Json jsonData)
    {
        var convertedData = new TableConvertedData();
        //Logic to convert to necessary datatype

        return convertedData;
    }

    public override IConvertedData ConvertXmlDataToOutput(XDocument xmlData)
    {
        var convertedData = new ImageConvertedData();
        //Logic to convert to necessary datatype

        return convertedData;
    }
}

public sealed class OtherDataConverter : DataConverter
{
    public OtherDataConverter()
        : base()
    {

    }

    public override IConvertedData ConvertJsonDataToOutput(Json jsonData)
    {
        var convertedData = new OtherConvertedData();
        //Logic to convert to necessary datatype

        return convertedData;
    }

    public override IConvertedData ConvertXmlDataToOutput(XDocument xmlData)
    {
        var convertedData = new OtherConvertedData();
        //Logic to convert to necessary datatype

        return convertedData;
    }
}

Ви можете додати ці реалізації за необхідності у міру розширення коду.

Інтерфейс IConvertedData дозволяє вам передати один тип у наступну фазу: ПРИМІТКА. Тут ви не можете повертати порожнечі. Він може байтом [] для зображень або документом OpenXml для WordDocument. Відрегулюйте по мірі необхідності.

public interface IConvertedData
{
    void RenderToPdf();
    void RenderToDocument();
    void RenderToOhter();
    void RenderToPowerPoint();
}

Поліморфізм:

Це використовується для перетворення даних у відповідний тип виводу. тобто візуалізація у PDF для даних зображень, може бути різною рендерінгою зображувальних даних для PowerPoint.

public sealed class ImageConvertedData : IConvertedData
{
    public void RenderToPdf()
    {
        //Logic to render Images
    }

    public void RenderToDocument()
    {
        //Logic to render Images
    }
}
public sealed class TableConvertedData : IConvertedData
{
    public void RenderToPdf()
    {
        //Logic to render Document
    }

    public void RenderToDocument()
    {
        //Logic to render Document
    }
}

public sealed class OtherConvertedData : IConvertedData
{
    public void RenderToPdf()
    {
        //Logic to render PDF
    }

    public void RenderToDocument()
    {
        //Logic to render PDF
    }
}

2 - Фабрика для визначення вихідного формату:

public enum ExportOutputType
{
    PDF,
    PowerPoint,
    Word,
    Other
}

public interface IOutputExporter
{
    void ExportData(IConvertedData data);
}


public class OutputExporterFactory
{
    public static IOutputExporter GetExportOutputType(ExportOutputType exportOutputType)
    {
        switch(exportOutputType)
        {
            case ExportOutputType.PDF:
                return new OutputExporterPdf();
            case ExportOutputType.PowerPoint:
                return new OutputExporterPowerPoint();
            case ExportOutputType.Other:
                return new OutputExporterOther();
            default:
                throw new Exception ("Unknown ExportOutputType");
        }
    }
}

Кожна конкретна реалізація розкриває загальний метод, який маскує спосіб відкидання експорту до реалізацій IConvertedData

public abstract class OutputExporter : IOutputExporter
{
    //Other base methods...
    public virtual void ExportData(IConvertedData data)
    {

    }
}

public sealed class OutputExporterPdf : OutputExporter
{
    public OutputExporterPdf()
        : base()
    {

    }

    public override void ExportData(IConvertedData data)
    {
        //Functionality to Export to Pdf
        data.RenderToPdf();
    }
}

public sealed class OutputExporterPowerPoint : OutputExporter
{
    public OutputExporterPowerPoint()
        : base()
    {

    }

    public override void ExportData(IConvertedData data)
    {
        //Functionality to Export to PowerPoint
        data.RenderToPowerPoint();
    }
}

public sealed class OutputExporterOther : OutputExporter
{
    public OutputExporterOther()
        : base()
    {

    }

    public override void ExportData(IConvertedData data)
    {
        //Functionality to Export to PowerPoint
        data.RenderToOhter();
    }
}

Прикладним клієнтом для всього цього було б:

public class Client
{
    public Client()
    {

    }
    public void StartExportProcess(XDocument data)
    {
        IDataConverter converter = DataConverterFactory.GetDataConverter(DataTypeToConvertTo.Graph);

        IConvertedData convertedData = converter.ConvertXmlDataToOutput(data);


        IOutputExporter exportOutputer = OutputExporterFactory.GetExportOutputType(ExportOutputType.PDF);
        exportOutputer.ExportData(convertedData);
    }
}

0

Тут ми вирішили подібну проблему: https://ergebnisse.zensus2011.de/?locale=en Там ми маємо в основному "таблиці" та "графіки", які потрібно експортувати в різних форматах: pdf, excel, web. Наша ідея полягала в тому, щоб визначити кожен об'єкт, який повинен бути представлений як власний клас Java з інтерфейсами для створення та читання цих класів. У вашому випадку для кожного об'єкта для створення (xml, json) буде 2 реалізації та 4 реалізації для візуалізації (читання).

Приклад: Вам знадобляться кілька класів для Таблиць: Таблиця класів (обробляє структуру таблиці, перевірку та вміст) Інтерфейс CreateTable (надає дані таблиці, комірки, проміжки, вміст) Інтерфейс ReadTable (геттери для всіх даних)

Напевно, вам не потрібні інтерфейси (або лише один), але я думаю, що це завжди забезпечує гарну розв'язку, особливо корисну при тестуванні.


0

Я думаю, що ви шукаєте - це стратегія . У вас є різні класи для виведення даних у бажаному форматі, і ви просто вибираєте відповідний під час виконання. Додавання нового формату повинно бути таким же простим, як і додавання іншого класу, який реалізує необхідний інтерфейс. Я робив це часто в Java, використовуючи Spring, щоб просто підтримувати карту перетворювачів, введених типом формату.

Як вже згадували інші, це, як правило, здійснюється, якщо всі класи реалізують один і той же інтерфейс (або походять з одного базового класу) і вибирають реалізацію через завод.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.