谁应该在这种情况下构建对象?

时间:2010-10-29 04:55:42

标签: c++ oop

我有以下课程:

class PluginLoader
{
public:
    PluginLoader(Logger&, PluginFactory&, ConflictResolver&);
    //Functions.
private:
    //Members and functions
};

LoggerPluginFactoryConflictResolver类都是将在应用程序中实现的接口类。我在主程序的顶层创建了单个PluginLoader对象。可以在该级别知道Logger。但是,PluginFactoryConflictResolver仅在PluginLoader类内部使用。因此,在顶层创建对象似乎很难看。我该怎么办?如果需要,我可以更改构造函数,但我希望保持原样。欢迎任何评论。感谢。

6 个答案:

答案 0 :(得分:2)

从您的问题和您的评论中看起来您似乎对两个看似相互矛盾的设计原则感到困惑。

  • 你应该尽可能地封装
  • 您不应该在实现中硬件创建所需的对象。

在您的情况下,您已经完成了第二项原则(至少从可见界面开始)。这是依赖注入的一个很好的例子。很高兴从创建PluginFactory和ConflictResolver的责任中重新设置PluginLoader。

现在,您认为您违反了第一原则,这在某种程度上是正确的。因此,请回过头来仔细重新审视您的设计问题。

PluginLoader究竟需要什么?它只需要引用 some PluginFactory和ConflictResolver对象。但它不需要自己创建它们。如何解决这个问题?

我能想到两种方式:

  • builder pattern中完成另一个抽象级别。这里 复杂的(或者显然复杂在你的情况下)建筑和 布线过程被移动到构建器对象。主程序不是 困扰着如何做到这一点。它只会触发建筑物 过程
  • 拥有另一个可以创建辅助对象的工厂 PluginFactory和ConflictResolver让PluginLoader只知道 关于这个新工厂的界面。主程序可以创建 这个工厂的具体实例并传递给PluginBuilder。

通过使用上述任一方法,您可以消除与原则2的冲突。

一切顺利。

答案 1 :(得分:1)

我认为解决方案是使用Dependency Injection机制:您可以通过仅更改字符串(可能是从配置文件加载)来选择具体的PluginFactoryConflictResolver

我在C ++中开发了一个名为Wallaroo的依赖注入库。在您的应用程序中,您可以使用以下代码声明您的PluginLoader类:

REGISTERED_CLASS( PluginLoader, void, void )
{
    ...
private:
    Plug< Logger > logger;
    Plug< PluginFactory > pluginFactory;
    Plug< ConflictResolver > conflictResolver;
};

然后,您可以决定要使用的具体类及其在xml文件中的连线:

<catalog>

  <!-- build the objects of your app -->

  <object>
    <name>loader</name>
    <class>PluginLoader</class>
  </object>

  <object>
    <name>logger</name>
    <class>MyConcreteLogger</class>
  </object>

  <object>
    <name>factory</name>
    <class>MyConcretePluginFactory</class>
  </object>

  <object>
    <name>resolver</name>
    <class>MyConcreteResolver</class>
  </object>

  <!-- wiring of the objects: -->

  <relation>
    <source>loader</source>
    <dest>logger</dest>
    <role>logger</role>
  </relation>

  <relation>
    <source>loader</source>
    <dest>factory</dest>
    <role>pluginFactory</role>
  </relation>

  <relation>
    <source>loader</source>
    <dest>resolver</dest>
    <role>conflictResolver</role>
  </relation>

</catalog>

或使用此代码:

catalog.Create( "loader", "PluginLoader" );
catalog.Create( "logger", "MyConcreteLogger" );
catalog.Create( "factory", "MyConcretePluginFactory" );
catalog.Create( "resolver", "MyConcreteResolver" );

wallaroo_within( catalog )
{
    use( "logger" ).as( "logger" ).of( "loader" );
    use( "factory" ).as( "pluginFactory" ).of( "loader" );
    use( "resolver" ).as( "conflictResolver" ).of( "loader" );
}

答案 2 :(得分:0)

我会让PluginLoader类有指向这些类的指针。我会转发声明类,我不会在实现文件中的任何地方定义类,以便没有其他人可见。这是更好的封装,加上这样做也减少了编译时间。

我不会使用原始指针,但auto_ptr,scoped_ptr或unique_ptr应该可以工作。

如果你在构造函数中创建这些对象,或者甚至在第一次使用时(延迟构造),那么你不需要将任何东西传递给构造函数,你可以删除这些参数。

编辑:

从其他评论中我发现我可能误解了你的问题。如果你想拥有实现接口的可自定义对象,但可以是下面的任何东西,那么你必须在顶层创建它们并传入接口引用/指针,就像你一样。

我认为您可以使用模板将类类型“传递”到对象中,以便它可以为自己创建这些类。但是,使用模板会使类类型在编译时得到修复,这可能不是您想要的。

答案 3 :(得分:0)

我的印象是你在构造函数中暴露内部的东西以进行可能的定制,但不是典型的情况。如果这是正确的,那么一个想法是使这些参数可选并在内部创建它们(如果没有提供)。

答案 4 :(得分:0)

  

...类是将在应用程序中实现的所有接口类...

     

... PluginFactoryConflictResolver仅在内部使用   PluginLoader班级......

这基本上意味着即使它们仅在内部使用,您也必须公开PluginFactoryConflictResolver的存在,因为您需要让客户端实现它们,然后将自定义实现传递给{{以某种方式(在这种情况下通过构造函数)。

然而,可以通过用指针替换引用来避免顶级命名对象的声明(在PluginLoader内部是静态的或自动的)(不知道为什么会想要这样做)(最好是聪明的kind,unique_ptr将只是工作的工具):

main

并像这样创建class PluginLoader { public: PluginLoader( std::unique_ptr<Logger> logger, std::unique_ptr<PluginFactory> plugin_factory, std::unique_ptr<ConflictResolver> conflict_resolver ) : logger(std::move(logger)) , plugin_factory(std::move(plugin_factory)) , conflict_resolver(std::move(conflict_resolver)) { } private: std::unique_ptr<Logger> logger, std::unique_ptr<PluginFactory> plugin_factory, std::unique_ptr<ConflictResolver> conflict_resolver };

PluginLoader

其中PluginLoader plugin_loader( std::unique_ptr<CustomLogger>(new CustomLogger(/* ... */)) std::unique_ptr<CustomPluginFactory>(new CustomPluginFactory(/* ... */)) std::unique_ptr<CustomConflictResolver>(new CustomConflictResolver(/* ... */)) ); CustomLoggerCustomPluginFactory分别是CustomConflictResolverLoggerPluginFactory的实现。

答案 5 :(得分:0)

我们对使用我们创建的dll的应用程序使用了类似的设计。记录器在主应用程序中已知,并在dll中使用。但是,有些对象仅在内部用于dll。如果它们仅在内部使用,我仍然不清楚为什么它们甚至是PluginLoader接口的一部分。其他对象的任何部分是否依赖于外部的那些接口?

我得到的是为什么不将工厂用于这些参数,PluginFactory,ConflictResolver并且根本不将它们作为参数传递?

e.g。      插件加载(记录器&安培);

然后在您的实施中

 PluginLoader(Logger& logger){
    Factory::PluginFactory::GetInstance().useInstance;// example
    Factory::ConflicResolver::GetInstance().useInstance;// exmaple
 }

或许你可以详细说明一下?