Merancang arsitektur yang kuat untuk berbagai jenis ekspor?

10

Saya mencari pola atau panduan arsitektur untuk fitur yang akan saya desain. Pada dasarnya, ini adalah fitur ekspor dengan beberapa target ekspor, dan saya mencari cara untuk membuatnya cukup generik di mana memasukkan target ekspor baru tidak memerlukan banyak perubahan inti. Dengan target ekspor, saya hanya merujuk pada berbagai jenis output, apakah itu PDF, presentasi PowerPoint, dokumen Word, RSS, dll. Saya memiliki kumpulan data, yang direpresentasikan dalam JSON dan XML. Data ini digunakan untuk membuat gambar (menggunakan nomor apa pun atau jenis ekspor [misalnya, PNG, JPG, GIF, dll.), Grafik, representasi tekstual, tabel, dan lainnya.

Saya mencoba menemukan cara untuk mengabstraksi semua rendering dan tata letak menjadi semacam rendering atau mesin tata letak yang menangani penambahan target ekspor lebih lanjut. Setiap bantuan / saran / sumber daya tentang cara pendekatan ini akan sangat dihargai. Terima kasih sebelumnya.

Untuk representasi gambar dari apa yang saya coba capai.

masukkan deskripsi gambar di sini

pengembang naif
sumber
Bisakah Anda menggambarkan apa yang telah Anda coba sejauh ini? Apa persyaratan (tanggung jawab) dari mesin tata letak? Misalnya, apakah ini diharapkan menangani pagination dan pemilihan ukuran halaman?
rwong
Dapatkah data XML / JSON digunakan untuk membuat beberapa tipe keluaran pada keluaran yang sama, yaitu data XML Anda menghasilkan Gambar dan Tabel dan Grafik dalam dokumen PDF? Atau bisakah data XML / JSON hanya digunakan untuk membuat Tabel atau Grafik untuk dokumen PDF?
Gibson
Ini semua tentang xkcd.com/927 - mengapa Anda mencoba menemukan kembali kemudi? DocBook, Penurunan Harga / pandoc dll. Sudah ada ...
Deer Hunter

Jawaban:

2

Bagi saya, cara untuk pergi adalah antarmuka dan Pabrik. Salah satu yang mengembalikan referensi ke antarmuka di mana berbagai kelas dapat disembunyikan. Kelas-kelas yang melakukan pekerjaan kasar yang sebenarnya semua harus terdaftar dengan Pabrik sehingga tahu kelas mana yang akan dipakai memberikan satu set parameter.

Catatan: alih-alih antarmuka Anda juga bisa menggunakan kelas basis abstrak, tetapi kekurangannya adalah bahwa untuk bahasa pewarisan tunggal itu membatasi Anda ke kelas basis tunggal.

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

Kode dalam sintaksis Delphi (Pascal) karena itu adalah bahasa yang paling saya kenal.

Setelah semua kelas implementasi terdaftar di pabrik, Anda harus dapat meminta referensi antarmuka ke turunan kelas semacam itu. Sebagai contoh:

Factory.GetReader('SomeFileName.xml');
Factory.GetWriter('SomeExportFileName.ppt');
Factory.GetRepresentation(rtTable, 'html');

harus mengembalikan referensi IReader ke instance TXMLReader; referensi IWriter ke instance TPowerPointWriter dan referensi IRepresentation ke instance THTMLTable.

Sekarang semua mesin rendering perlu lakukan, adalah mengikat semuanya:

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;

Antarmuka IReader harus menyediakan metode untuk membaca data yang dibutuhkan oleh pelaksana IRepresentation untuk membangun representasi data. Demikian pula, perwakilan IRC harus menyediakan metode yang perlu diterapkan oleh pelaksana IWriter untuk mengekspor representasi data ke format file ekspor yang diminta.

Dengan asumsi data dalam file Anda bersifat tabular, IReader dan antarmuka pendukungnya dapat terlihat seperti:

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;

Iterasi di atas meja akan menjadi masalah

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;

Karena representasi dapat berupa gambar, grafik, dan tekstual, presentasi IR mungkin akan memiliki metode yang mirip dengan IReader untuk melintasi tabel yang dikonstruksi dan akan memiliki metode untuk mendapatkan gambar dan grafik, misalnya sebagai aliran byte. Terserah pelaksana IWriter untuk menyandikan nilai tabel dan byte gambar / grafik seperti yang dipersyaratkan oleh target ekspor.

Marjan Venema
sumber
1

Sementara saya setuju bahwa lebih banyak informasi diperlukan untuk berpikir tentang arsitektur, cara paling sederhana untuk membuat berbagai jenis objek yang berperilaku sama (yaitu semuanya akan menghasilkan output) menggunakan pola pabrik. Info lebih lanjut di sini

Pola metode pabrik adalah pola desain kreatif berorientasi objek untuk menerapkan konsep pabrik dan menangani masalah menciptakan objek (produk) tanpa menentukan kelas objek yang tepat yang akan dibuat. Inti dari pola ini adalah untuk "Tentukan antarmuka untuk membuat objek, tetapi biarkan kelas yang mengimplementasikan antarmuka memutuskan kelas mana yang akan dipakai. Metode Pabrik memungkinkan kelas menunda instantiasi ke subclass." Dari wikipedia

Orposuser
sumber
1
Saya pikir ini sedikit lebih terlibat dari itu. Misalnya, protokol apa yang akan digunakan untuk mengkomunikasikan data di sepanjang garis dalam diagram? Mungkinkah ada representasi data umum dalam mesin Rendering / tata letak, atau apakah mesin itu hanya pabrik untuk metode sepenuhnya kustom, satu untuk setiap baris dalam diagram?
Robert Harvey
Tidak yakin Jika saya mengerti maksud Anda di sini. Karena Jika perlu menggunakan protokol untuk mengkomunikasikan garis-garis dalam diagram maka saya berpikir bahwa saya mengandalkan seperangkat layanan untuk menghasilkan ekspor (dalam hal ini Anda ingin melihat beberapa pola soa / integrasi). Bahkan jika ini benar solusinya fleksibel dan cukup kuat untuk menggunakan pabrik. Mungkin hal yang ingin Anda lakukan adalah membuat antarmuka konverter yang memiliki dua metode: satu yang menerima data XML dan satu lagi untuk data JSON. Objek kembali untuk keduanya akan menjadi objek yang dikonversi. Dengan begitu Anda dapat mengumpulkan apa pun yang Anda inginkan.
Pengguna Orpos
sebenarnya ada dua pertanyaan di header: tentang konten (gif, pdf, html) dan tentang transportasi (file lokal, http-response-item). Untuk memperluas @Orposuser (+1), jawab: Saya akan membuat aliran menggunakan pabrik yang dapat dengan mudah tidak dikirim dan mudah dibuat untuk tanggapan http.
k3b
0

Anda bisa berakhir dengan sesuatu seperti ini.

Kedua pabrik tersebut berpusat di:

1 - untuk mengonversi tipe input (Json / XML) ke implementasi konkret tentang bagaimana mengkonversi data ini ke gambar / grafik

2 - Pabrik kedua untuk memutuskan bagaimana membuat output ke Dokumen kata / Dokumen PDF

Polimorfisme menggunakan antarmuka umum untuk semua data yang diberikan. Jadi gambar / tabel dapat dipindahkan sebagai antarmuka yang mudah.

1 - Pabrik untuk mengkonversi data JSON / XML ke implementasi konkret:

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

Pabrik di bawah ini memungkinkan Anda untuk mengkonversi Data xml atau Data Json ke jenis beton yang benar.

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");
        }
    }
}

Implementasi konkret melakukan semua pekerjaan berat dari data yang dikonversi ke tipe yang relevan. Mereka juga mengkonversi data ke antarmuka IConvertedData, yang digunakan untuk polimorfisme.

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;
    }
}

Anda dapat menambahkan implementasi ini seperlunya, saat kode Anda diperluas.

Antarmuka IConvertedData memungkinkan Anda memasukkan satu jenis ke fase berikutnya: CATATAN: Anda mungkin tidak akan mengembalikan void di sini. Bisa dengan byte [] untuk gambar atau dokumen OpenXml untuk WordDocument. Sesuaikan seperlunya.

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

Polimorfisme:

Ini digunakan untuk mengonversi data ke tipe keluaran yang relevan. yaitu rendering ke PDF untuk data gambar, mungkin berbeda rendering data gambar untuk 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 - Pabrik untuk menentukan format output:

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");
        }
    }
}

Setiap implementasi konkret memperlihatkan metode umum yang menutupi bagaimana ekspor dikembalikan ke implementasi 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();
    }
}

Klien sampel untuk semua ini adalah:

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);
    }
}
Gibson
sumber
0

Kami memecahkan masalah serupa di sini: https://ergebnisse.zensus2011.de/?locale=en Di sana kami memiliki sebagian besar "tabel" dan "grafik" untuk diekspor dalam format yang berbeda: pdf, excel, web. Ide kami adalah untuk menentukan setiap objek yang akan dirender sebagai kelas Java sendiri dengan antarmuka untuk membuat dan membaca kelas-kelas tersebut. Dalam kasus Anda akan ada 2 implementasi untuk setiap objek untuk membuat (xml, json) dan 4 implementasi untuk rendering (membaca).

Contoh: Anda akan memerlukan beberapa kelas untuk Tabel: Tabel Kelas (menangani struktur tabel, validasi dan konten) Interface CreateTable (menyediakan data tabel, sel, rentang, konten) Interface ReadTable (getter untuk semua data)

Mungkin Anda tidak memerlukan antarmuka (atau hanya satu) tetapi saya pikir itu selalu memberikan decoupling yang baik terutama berguna dalam pengujian.

dermoritz
sumber
0

Saya pikir apa yang Anda cari adalah pola Strategi . Anda memiliki berbagai kelas untuk menampilkan data dalam format yang diinginkan, dan Anda cukup memilih yang sesuai saat runtime. Menambahkan format baru harus sesederhana menambahkan kelas lain yang mengimplementasikan antarmuka yang diperlukan. Saya sudah sering melakukan ini di Jawa menggunakan Spring untuk hanya mempertahankan peta konverter, diketik oleh jenis format.

Seperti yang telah disebutkan orang lain, ini biasanya dilakukan dengan meminta semua kelas mengimplementasikan antarmuka yang sama (atau turun dari kelas dasar yang sama) dan memilih implementasi melalui pabrik.

TMN
sumber