朋友,抽象类和工厂模式

时间:2013-01-16 07:49:20

标签: c++ inheritance factory-pattern friend

祝大家好日子......

我正在为我公司的一个复杂项目工作,我在项目中使用了一些被榨干的工厂设计模式。省略细节;我有一些课程(我称之为“设备”)只能由“读者”创建:

class DeviceBase // this is a virtual base class
{
  public:
   //some stuff
   friend class ReaderBase; // this is OK and necessary I guess?
  private:
   DeviceBase(); // cannot create a device directly
   //some more stuff
}

class Device1: public DeviceBase // some extended device
{
  public:
   //some stuff 
  private:
   //some more stuff
}

class Device2: public DeviceBase  // some other extended device
{
  public:
   //some stuff

  private:
   //some more stuff
}

现在是“读者”,恰好是设备的工厂:

class ReaderBase
{
  private:
    DeviceBase[] _devices; // to keep track of devices currently "latched"
  public:
    // some other methods, getters-setters etc ...

    // this method will create the "Devices" :
    virtual bool PollforDevice ( DeviceType, timeout) = 0; 

}

现在,这是我的工厂类;但它(如你所见)纯虚拟。我有特殊的读者继承了这个:

 class InternalReader: public ReaderBase
 {
   public:
     // define other inherited methods by specifics of this reader
     bool PollforDevice( DeviceType dt, timeout ms)
     {
         switch(dt)
         {
           case Device1: { /* create new device1 and attach to this reader */ } break;
           case Device2: { /* create new device2 and attach to this reader */ } break;
         }
         // show goes on and on...
     }
 }

 class ExternalReader: public Reader
 {
   public:
     // define other inherited methods by specifics of this reader
     bool PollforDevice( DeviceType dt, timeout ms)
     {
         switch(dt)
         {
           case Device1: { /* create new device1 and attach to this reader */ } break;
           case Device2: { /* create new device2 and attach to this reader */ } break;
         }
         // show goes on and on...
     }
 }

我使用这种模式的原因是:我正在编写一个系统,可以同时连接多个这些“读者”,我必须同时使用它们。

这些“设备”:我也可以把他们的建设者公之于众,每个人都会高兴;但我想确保它们不是由代码编写者自己创建的(以确保它的其他编码器)

现在问题:

  1. 我应该在每个“设备”中明确声明ReaderBase是朋友吗?或者只是在“DeviceBase”基础上声明应该就够了吗?
  2. 我是否应该明确地将每个“设备”中的“读者”继承自“ReaderBase”也是这些设备的朋友,或者只是将ReaderBase放入其中就足够了?
  3. 我可以(而且我应该)将成员方法“PollforDevice”变成朋友,而不是让整个“ReaderBase”类成为朋友吗?知道这是一个纯粹的虚拟方法,那还能成为继承副本的朋友吗?
  4. 我很抱歉这个问题很长,但我只想说清楚。

    提前致谢...

2 个答案:

答案 0 :(得分:2)

为什么要担心像DeviceBase这样的纯抽象基类的可构造性呢?如果它是一个设计合理的合同或抽象基类,它无论如何都无法构建。除非你必须适应你没有提到的某种框架,否则只需要隐藏,例如:

struct DeviceBase {
    virtual void Foo() = 0;
    virtual void Bar() = 0;
    virtual ~DeviceBase() = default;
};

顺便说一句,声明构造函数或析构函数private将非常有效地使您的类“密封”。如果出于某种原因DeviceBase不是抽象的(这在我看来是一个严重的设计缺陷),那么构造函数protected就不会private。您需要打扰的地方是具体Device类的构造函数可访问性。假设您要“发布”这些实现类(即它们的定义可供您的库用户访问),并且您希望强调禁止直接构造,请使用“访问惯用语”(我的发明名称):< / p>

namespace impl_detail {
    class DeviceAccess;
}

class ConcreteDevice1 : public DeviceBase {
    friend class impl_detail::DeviceAccess;
    // implementation of DeviceBase and all other stuff go 
    // into the "private" section
};

namespace impl_detail {
    class DeviceAccess {
        template< class TDevice >
        static DeviceBase* Create()
        {
            return new TDevice;
        }
    };
};

Reader班级中,使用impl_detail::DeviceAccess::Create构建Device个实例,例如:

// Your ExternalReader::PollForDevice...
switch (dt) {
    case Device1:
        return impl_detail::DeviceAccess::Create<ConcreteDevice1>();
    case Device2: 
        // etc...
}

长话短说,最好的解决方案是根本不发布具体的实施课程,其次是限制建设的某种“心理障碍”,例如:以上那种...

答案 1 :(得分:1)

  
      
  1. 我应该在每个“设备”中明确声明ReaderBase是朋友吗?或者只是在“DeviceBase”基础上声明应该就够了吗?
  2.   
  3. 我是否应该明确地将每个“设备”中的“读者”继承自“ReaderBase”也是这些设备的朋友,或者只是将ReaderBase放入其中就足够了?
  4.   

由于友谊不是继承的(友谊关系的任何一方),你的方案唯一的工作方式是在每个派生的设备中声明每个派生的读者的友谊。这会在Reader类和Device类之间产生紧密耦合,这不是一个很好的设计。

  

3)不是让整个“ReaderBase”类成为朋友,我(并且应该)只是将成员方法“PollforDevice”变成朋友吗?知道这是一个纯粹的虚拟方法,是否也会成为继承副本的朋友?

你可以让ReaderX::PollforDevice成为朋友而不是整个ReaderX类,但它对你没什么帮助,只会为难以解决循环依赖关系打开大门。


实际上,很难创建一种设计,其中层次结构X的类只能由层次结构Y的类创建而不能由其他人创建,而不会在两个层次结构中的类之间创建紧密耦合。 我的方法是

  1. 首先,教育你的同事,如果他们想要DeviceX,那么他们可以从ReaderY获得它,而不是以其他方式。确保在代码审查中强制执行此操作。 所有其他步骤都只是损害控制。
  2. 确保只有BaseDevice类暴露给Reader实现之外的代码。
  3. 保护所有Device类的析构函数。这确保了Device类只能由派生类或朋友清理(并自动排除非朋友的堆栈分配)。如果他们不小心尝试直接使用Device类,应该让某人三思而后。
  4. ReaderBase成为DeviceBase的朋友,并为ReaderBase提供一个功能,以便对设备进行实际清理。这是确保清除设备
  5. 所必需的。