여러 내보내기 유형을위한 견고한 아키텍처를 설계하십니까? 할 수있는 방법을 찾고 있습니다.

디자인중인 다음 기능에 대한 패턴 또는 아키텍처 지침을 찾고 있습니다. 기본적으로 여러 내보내기 대상이있는 내보내기 기능이며 새 내보내기 대상을 연결하는 데 많은 핵심 변경이 필요하지 않은 경우 일반화 할 수있는 방법을 찾고 있습니다. 내보내기 대상을 사용하면 PDF, PowerPoint 프레젠테이션, Word 문서, RSS 등 다양한 유형의 출력을 간단히 언급하고 있습니다. JSON 및 XML로 표시되는 기본 데이터 세트가 있습니다. 이 데이터는 이미지 (임의의 숫자 또는 내보내기 유형 (예 : PNG, JPG, GIF 등) 사용), 그래프, 텍스트 표현, 표 등을 구성하는 데 사용됩니다.

추가 렌더링 대상 추가를 처리하는 렌더링 또는 레이아웃 엔진으로 모든 렌더링 및 레이아웃을 추상화하는 방법을 찾으려고합니다. 이에 접근하는 방법에 대한 도움 / 제안 / 자원은 대단히 감사하겠습니다. 미리 감사드립니다.

내가 달성하려는 것을 그림으로 표현하십시오.

여기에 이미지 설명을 입력하십시오



답변

저에게는 갈 길은 인터페이스와 팩토리입니다. 하나는 다양한 클래스가 숨길 수있는 인터페이스에 대한 참조를 반환합니다. 실제 grunt 작업을 수행하는 클래스는 모두 팩토리에 등록해야하므로 주어진 매개 변수 세트를 인스턴스화 할 클래스를 알 수 있습니다.

참고 : 인터페이스 대신 추상 기본 클래스를 사용할 수도 있지만 단일 상속 언어의 경우 단일 기본 클래스로 제한한다는 단점이 있습니다.

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 (파스칼) 구문으로되어 있습니다.

모든 구현 클래스가 팩토리에 등록되면 해당 클래스의 인스턴스에 대한 인터페이스 참조를 요청할 수 있어야합니다. 예를 들면 다음과 같습니다.

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 구현 자에 달려 있습니다.


답변

아키텍처에 대해 더 많은 정보가 필요하다는 데 동의하지만 동일하게 동작하는 다른 종류의 객체를 만드는 가장 간단한 방법 (즉, 모든 객체가 출력을 생성 함)은 팩토리 패턴을 사용하는 것입니다. 더 많은 정보는 여기에

팩토리 메소드 패턴은 팩토리의 개념을 구현하고 작성 될 정확한 클래스의 오브젝트를 지정하지 않고 오브젝트 (제품) 작성 문제를 처리하기위한 오브젝트 지향 작성 디자인 패턴입니다. 이 패턴의 본질은 “객체를 만들기위한 인터페이스를 정의하지만 인터페이스를 구현하는 클래스가 인스턴스화 할 클래스를 결정하게합니다. Factory 메소드는 클래스가 서브 클래스에 대한 인스턴스화를 지연시킬 수 있도록합니다.”
위키 백과에서


답변

이런 식으로 끝날 수 있습니다.

두 공장은 다음을 기반으로합니다.

1-입력 유형 (Json / XML)을이 데이터를 이미지 / 그래프로 변환하는 구체적인 구현으로 변환

2-출력을 워드 문서 / PDF 문서로 렌더링하는 방법을 결정하기위한 두 번째 팩토리

다형성은 렌더링 된 모든 데이터에 공통 인터페이스를 사용합니다. 따라서 이미지 / 테이블을 간편한 인터페이스로 이동할 수 있습니다.

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 인터페이스를 사용하면 단일 유형을 다음 단계로 전달할 수 있습니다. 참고 : 여기서 공백을 반환하지 않을 수 있습니다. 이미지의 경우 바이트 [] 또는 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);
    }
}

답변

https://ergebnisse.zensus2011.de/?locale=ko 여기에서 비슷한 문제를 해결했습니다
. pdf, excel, web과 같은 다른 형식으로 내보낼 수있는 “테이블”과 “그래프”가 있습니다. 우리는 각 객체를 클래스를 생성하고 읽을 수있는 인터페이스가있는 자체 Java 클래스로 렌더링하도록 지정했습니다. 귀하의 경우 각 객체마다 2 개의 구현 (xml, json)이 구현되고 4 개의 렌더링 (읽기)이 구현됩니다.

예 : 테이블에 대한 몇 가지 클래스가 필요합니다. 클래스 테이블 (테이블 구조, 유효성 검증 및 컨텐츠 처리) 인터페이스 CreateTable (테이블 데이터, 셀, 범위, 컨텐츠 제공) 인터페이스 ReadTable (모든 데이터에 대한 getter)

아마도 인터페이스 (또는 하나만)가 필요하지는 않지만 테스트에서 특히 유용한 분리를 항상 제공한다고 생각합니다.


답변

당신이 찾고있는 것은 전략 패턴 이라고 생각합니다 . 원하는 형식으로 데이터를 출력하는 다양한 클래스가 있으며 런타임에 적절한 클래스를 선택하기 만하면됩니다. 새로운 형식을 추가하는 것은 필요한 인터페이스를 구현하는 다른 클래스를 추가하는 것만 큼 간단해야합니다. Java를 사용하여 Spring을 사용하여 종종 형식 유형별로 키가 지정된 변환기 맵을 유지 관리하기 위해이 작업을 자주 수행했습니다.

다른 사람들이 언급했듯이 이것은 일반적으로 모든 클래스가 동일한 인터페이스를 구현하거나 동일한 기본 클래스의 하위 클래스로 구현하고 팩토리를 통해 구현을 선택하여 수행됩니다.