複数のエクスポートタイプ用の堅牢なアーキテクチャを設計していますか?


10

設計している次の機能のパターンまたはアーキテクチャガイダンスを探しています。基本的に、これは複数のエクスポートターゲットを備えたエクスポート機能であり、新しいエクスポートターゲットをプラグインする際に多くのコア変更を必要としない場合に十分に汎用的にする方法を探しています。エクスポートターゲットでは、PDF、PowerPointプレゼンテーション、Wordドキュメント、RSSなど、さまざまな種類の出力を単に参照しています。JSONとXMLで表されるデータの基本セットがあります。このデータは、画像(任意の数またはエクスポートタイプ(PNG、JPG、GIFなど)を使用)、グラフ、テキスト表現、表などを作成するために使用されます。

私は、すべてのレンダリングとレイアウトを、さらにエクスポートターゲットの追加を処理するある種のレンダリングまたはレイアウトエンジンに抽象化する方法を見つけようとしています。これにどのように取り組むかについてのヘルプ/提案/リソースは大歓迎です。前もって感謝します。

私が達成しようとしていることを図で表したもの。

ここに画像の説明を入力してください


これまでに試みたことを説明できますか?レイアウトエンジンの要件(責任)は何ですか?たとえば、ページネーションやページサイズの選択を処理することは期待されていますか?
rwong 2013

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');

TXMLReaderのインスタンスへのIReader参照を返す必要があります。TPowerPointWriterのインスタンスへのIWriter参照、およびTHTMLTableのインスタンスへのIRepresentation参照。

これで、レンダリングエンジンが行う必要があるのは、すべてを結合することです。

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

アーキテクチャについて考えるにはより多くの情報が必要であることに同意しますが、同じように動作する(つまり、すべてのオブジェクトが出力を生成する)異なる種類のオブジェクトを作成する最も簡単な方法は、ファクトリパターンを使用することです。詳細はこちら

ファクトリー・メソッド・パターンは、ファクトリーの概念を実装するオブジェクト指向の作成デザイン・パターンであり、作成されるオブジェクトの正確なクラスを指定せずにオブジェクト(製品)を作成する問題に対処します。このパターンの本質は、「オブジェクトを作成するためのインターフェースを定義しますが、インターフェースを実装するクラスがインスタンス化するクラスを決定できるようにすることです。Factoryメソッドは、クラスがインスタンス化をサブクラスに遅延させるようにします。」 ウィキペディアから


1
それはもう少し複雑だと思います。たとえば、図の線に沿ってデータを通信するためにどのプロトコルが使用されますか?レンダリング/レイアウトエンジンに共通のデータ表現がありますか、それともそのエンジンは、図の各行に1つずつ、完全にカスタムメソッドのファクトリにすぎませんか?
Robert Harvey

ここであなたのポイントを得るかどうかはわかりません。ダイアグラムのラインを通信するためにプロトコルを使用する必要がある場合、私はエクスポートを生成するために一連のサービスに依存していると考えています(この場合、いくつかのsoa /統合パターンを確認したいと思います)。これが真実であっても、ソリューションは柔軟であり、ファクトリを使用するのに十分なほど堅牢です。多分あなたがしたいことは、2つのメソッドを持つコンバーターインターフェースを作成することです:1つはXMLデータを受け取るもので、もう1つはJSONデータ用です。両方の戻りオブジェクトは、変換されたオブジェクトになります。そうすれば、好きなように組み立てることができます。
Orposuser 2013年

ヘッダーには実際には2つの質問があります。コンテンツ(gif、pdf、html)とトランスポート(ローカルファイル、http-response-item)です。@Orposuser(+1)の回答を拡張するには:ファクトリーを使用してストリームを作成し、ユニットテストを簡単に実行して、http応答用に簡単にレンダリングできるようにします。
k3b

0

あなたはこのようなもので終わるかもしれません。

2つの工場は、次の拠点に基づいています。

1-入力タイプ(Json / XML)を、このデータを画像/グラフに変換する方法の具体的な実装に変換するため

2-出力をワードドキュメント/ PDFドキュメントにレンダリングする方法を決定するための2番目のファクトリ

ポリモーフィズムは、すべてのレンダリングデータに共通のインターフェイスを使用します。そのため、簡単なインターフェースとして画像/テーブルを移動できます。

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データまたは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インターフェイスを使用すると、単一の型を次のフェーズに渡すことができます。注:ここでvoidを返すことはできません。画像の場合はbyte []、WordDocumentの場合はOpenXmlドキュメントを使用できます。必要に応じて調整してください。

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、エクセル、ウェブ:私たちは、ほとんどが「テーブル」と異なるフォーマットにエクスポートする「グラフ」をそこに持っています。私たちのアイデアは、それらのクラスを作成および読み取るためのインターフェースを持つ独自のJavaクラスとしてレンダリングされる各オブジェクトを指定することでした。あなたのケースでは、作成(xml、json)の各オブジェクトに2つの実装と、レンダリング(読み取り)の4つの実装があります。

例:テーブルにいくつかのクラスが必要です:クラステーブル(テーブル構造、検証、コンテンツを処理)インターフェースCreateTable(テーブルデータ、セル、スパン、コンテンツを提供)インターフェースReadTable(すべてのデータのゲッター)

おそらくインターフェイスは必要ありません(または1つだけ)が、テストで特に有用な優れたデカップリングが常に提供されると思います。


0

あなたが探しているのは戦略パターンだと思います。希望する形式でデータを出力するためのさまざまなクラスがあり、実行時に適切なクラスを選択するだけです。新しいフォーマットの追加は、必要なインターフェースを実装する別のクラスを追加するのと同じくらい簡単でなければなりません。私はこれをJavaでSpringを使用して頻繁に実行し、フォーマットタイプによってキー付けされたコンバーターのマップを単純に維持しました。

他の人が述べたように、これは通常、すべてのクラスが同じインターフェイスを実装する(または同じ基本クラスから派生する)ことと、ファクトリを介して実装を選択することによって達成されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.