【问题标题】:Design pattern advice on this project for a table archiver表存档器的此项目的设计模式建议
【发布时间】:2012-04-28 01:59:12
【问题描述】:

所以我有以下项目要完成。我还没有决定如何设计它。希望得到一些建议。

它基本上是一个表格存档器。给定特定条件,您需要将这些行导出到另一个地方。 这个地方可以是另一个数据库或(s)ftp 服务器。 如果您选择一个数据库,您需要在每次达到特定限制时创建一个表(例如每个表不超过 50k 行),如果您选择一个 (s)ftp 服务器,那么您需要编写一个 CSV 或 XML 并将文件。

所以我们有这些组合:

  1. sql2CustomerSql
  2. sql2Oursql(为此,我们已经根据系统配置进行了连接和获取一些信息的类)
  3. csv2ftp
  4. csv2sftp
  5. xml2ftp
  6. xml2sftp

现在,我到处都看到 AbstractFactory 模式,但基于什么? 我的想法是我应该拥有 SQLWriter、XMLWriter、CSVWriter,它们都继承自一个抽象 Writer 类,该类实现一些常见的策略,如计算行数、获取常见的配置参数等...... 我应该对 Connection 类/接口做同样的事情吗(因为 sql 和 (s)ftp 真的不同?

如果您需要更多信息,请询问。

【问题讨论】:

    标签: design-patterns architecture


    【解决方案1】:

    听起来你是在正确的道路上。

    您应该避免创建由 Writer 和 Connection 组合而成的类,而是创建某种包含(作为属性)Writer 接口和 Connection 接口的 Manager 类。然后为每个创建适当的实现并将它们传递给 Manager。

    这是策略设计模式的经典用法。

    编辑:添加代码示例。您应该添加适当的错误检查。

    class Writer
    {
    public:
        virtual void output(cons std::string &data) = 0;
    };
    
    class Format
    {
    public:
        virtual std::string serialize() = 0;
    };
    
    // Create concrete Writer and Format classes here
    
    class OutputManager
    {
    public:
        // Notice there should be no Writer, Format creation logic here,
        // This class should focus on orchestrating the output
        OutputManager() : writer_(NULL), format_(NULL) {}
        OutputManager(Writer *w, Format *f) : writer_(w), format_(f) {}
    
        void setWriter(Writer *w) { writer_ = w; }
        Writer *getWriter()       { return writer_; }
    
        void setFormat(Format *f) { format_ = f; }
        Format *getFormat()       { return format_; }
    
        // Maybe this should have a different return type
        void doOutput()
        {
            // Not sure what else you would need here,
            // but this is an example usage
            writer_->output(format_->serialize());
        }
    
    private:
        Writer *writer_;
        Format *format_;
    };
    
    //
    // And now the factories
    //
    class OutputAbstractFactory
    {
    public:
        OutputAbstractFactory(Config *c) config_(c) {}
        void createFactories()
        {
            writerFactory_ = WriterAbstractFactory::getWriterFactory(config_);
            formatFactory_ = FormatAbstractFactory::getFormatFactory(config_);
        }
    
        Writer *getWriter() { return writerFactory_->getWriter(); }
        Format *getFormat() { return formatFactory_->getFormat(); }
    
    private:
        WriterAbstractFactory *writerFactory_;
        FormatAbstractFactory *formatFactory_;
        Config *config_;
    }
    
    class WriterAbstractFactory
    {
    public:
        // Config is a class you will have to make with 
        // the info detailing the Writer and Format stuff
        static WriterAbstractFactory *getWriterFactory(Config *c);
        virtual Writer *getWriter() = 0;
    };
    
    class FormatAbstractFactory
    {
    public:
        // Config is a class you will have to make with
        // the info detailing the Writer and Format stuff
        static FormatAbstractFactory *getFormatFactory(Config *c);
        virtual Format *getFormat() = 0;
    };
    
    // Create concrete factories here
    
    //
    // And this ties it all together
    //
    int main(int argc, char **argv)
    {
        Config c;
        // populate Config accordingly
    
        OutputAbstractFactory *factory(&c);
        factory.createFactories();
    
        Writer *w = factory->getWriter();
        Format *f = factory->getFormat();
        // Do whatever else you need to with the Writer/Format here
    
        OutputManager om(w, f);
        // Do whatever else you need with the OutputManager here
        om.doOutput();
    }
    

    【讨论】:

    • 所以你说Manager类应该使用策略模式来处理配置,而我应该通过AbstractFactory创建Writers和Connections的实例?
    • 我想我可以在我刚刚添加的代码中更好地解释它:) 但基本上我们使用的是策略模式来避免混合类,以及用于编写器/格式创建逻辑的 AbstractFactory。 OutputManager 可能有点矫枉过正,它只取决于您还需要对 Writer 和 Formats 做什么。
    • 我猜你的意思是 Writer/Connection :) 谢谢,真的很有趣。
    • 我决定和你一起去。我对 Connection 部分做了一些小的改动,因为我需要使用一些已经存在的东西并且它们没有通用接口。 Basicalley 我为 SQLWriter 选择了一个 java.sql.connection,我为其他两个 Writer 选择了一个“传输”接口(它是已经用于 FTP、SFTP、FTPS 等的接口)。基本上实例化比你的低一级。
    【解决方案2】:

    您可能想要获取一份Modern C++ Design 的副本,以了解如何制作基于策略的通用转换工具。下面是一个非常粗略的框架,它根据两个策略进行了参数化:格式和连接。

    template
    <
        typename FormatPolicy,    // SQL, XML, CSV, provides row, config functionality
        typename ConnectionPolicy // SQL, FTP, provides open/close/read/write functionality
    >
    class ConversionTool
    : 
        public FormatPolicy,
        public ConnectionPolicy
    {
    public:
        // your conversion interface here
        void operator()( /* your input */, /* your output */);
    };
    
    class SQLFormat { // SQL specific stuff } ;
    class SQLConnection { // SQL specific stuff };
    
    typedef ConversionTool<SQLFormat, SQLConnection> SQL2OurCustomerSQL;
    SQL2OurCustomerSQL()(in_file, out_file); // for elsewhere declared in_file / out_file
    

    【讨论】:

    • 我会尽量避免像您在 ConversionTool 中那样的多重继承。策略模式基本上是在谈论使用“HAS-A”关系而不是“IS-A”关系。
    • 这纯粹是接口的多重继承,ConnectionTool的类型参数覆盖了定制的正交方向,而且它们通常是无状态的,所以不存在菱形图等问题。再看Modern C++的解释。
    【解决方案3】:

    您正在处理两个问题。一个是格式,另一个是持久化的目的地。对于格式,您需要序列化程序,对于目标,您需要适配器。 SqlSerializer、XMLserializer。 SQLDestination, FTPDestination atc。

    伪代码如下所示:

    • Destination.persist(new Serializer(SourceData));
    • Destination.persist(Serializer.transform(SourceData));
    • new Destination(new Serializer(SourceData)).persist();

      interface DataSerializer{
          String serialize();
      }
      
      class SqlSerializer implements DataSerializer {
          final Object rawData;
      
          SqlSerializer(Object rawData) {
              this.rawData = rawData;
          }
      
          String serialize() {
              // SQL Serialization Logic
          }
      }
      
      class XmlSerializer implements DataSerializer {
          final Object rawData;
      
          XmlSerializer(Object rawData) {
             this.rawData = rawData;
          }
      
          String serialize() {
              // XML Serialization Logic
          }
      }
      
      interface Destination {
          void persist(DataSerializer dataSerializer);
      }
      
      class SqlDestination implements Destination {
          final String username;
          ...
          ...
      
          SqlDestination(String username, String password, String url) {
              this.username = username;
              ...
              ...
          }
      
          void persist(DataSerializer dataSerializer) {
              // open SQL connection
              String = dataSerialize.serialize();
              // do sqlPersistanceLogic
          }
      }
      
      class FtpDestination implements Destination {
          final String username;
          ...
          ...
      
          FtpDestination(String username, String password, String url) {
              this.username = username;
              ...
              ...
          }
      
          void persist(DataSerializer dataSerializer) {
              // open FTP session connection
              String = dataSerialize.serialize();
              // send to ftpDestination
          }
      }
      
      Call:
      
      Destination dest = getApropriateDestination();
      dest.persist(new XmlSerializer(rawData));
      

    您还可以实现逻辑来验证特定目标支持的序列化程序,或者如果目标和序列化程序之间存在 1:1 关系,您可以使用模板。但我不知道它在 C++ 中是怎样的;

    根据您使用的工具和框架,您可以实现三种建议的方式之一。原则不变

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-20
      • 1970-01-01
      • 2017-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-08
      • 1970-01-01
      相关资源
      最近更新 更多