C++ 朋友、抽象类和工厂模式

C++ 朋友、抽象类和工厂模式,c++,inheritance,factory-pattern,friend,C++,Inheritance,Factory Pattern,Friend,祝你们大家好 我正在我公司的一个复杂项目中工作,我在项目中使用了一些扭曲的工厂设计模式。省略细节;我有一些只能由“阅读器”创建的类(我称之为“设备”): 现在是“阅读器”,它恰好是设备的工厂: class ReaderBase { private: DeviceBase[] _devices; // to keep track of devices currently "latched" public: // some other methods, getters-set

祝你们大家好

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

现在是“阅读器”,它恰好是设备的工厂:

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...
     }
 }
我使用这种模式的原因是:我为一个可以同时连接多个“读卡器”的系统而写,我必须同时使用它们

还有这些“装置”:我也可以公开它们的构造器,每个人都会很高兴;但我想确保它们不是由代码编写者自己创建的(以确保其他代码编写者不会这样做)

现在问题是:

  • 我是否应该在每个“设备”中明确声明ReaderBase是朋友?或者仅仅在基础上声明“DeviceBase”就足够了
  • 我是否应该明确地将从“ReaderBase”继承的“Reader”也作为这些设备的朋友放入每个“设备”,或者仅仅放入ReaderBase就足够了
  • 与其让整个“ReaderBase”类成为朋友,我可以(也应该)让成员方法“PollforDevice”成为朋友吗?知道这是一个纯粹的虚拟方法,那么继承副本也会成为朋友吗
  • 很抱歉,这个问题很长,但我只想说清楚

    先谢谢你

  • 我是否应该在每个“设备”中明确声明ReaderBase是朋友?或者仅仅在基础上声明“DeviceBase”就足够了
  • 我是否应该明确地将从“ReaderBase”继承的“Reader”也作为这些设备的朋友放入每个“设备”,或者仅仅放入ReaderBase就足够了
  • 由于友谊不是继承的(在友谊关系的任何一方),所以方案工作的唯一方式是在每个派生设备中声明每个派生读取器的友谊。这在读卡器类和设备类之间创建了紧密耦合,这不是一个好的设计

    3) 与其让整个“ReaderBase”类成为朋友,我可以(也应该)让成员方法“PollforDevice”成为朋友吗?知道这是一个纯粹的虚拟方法,那么继承副本也会成为朋友吗

    您可以让
    ReaderX::PollforDevice
    成为朋友,而不是整个
    ReaderX
    类,但这对您帮助不大,只会为难以解决的循环依赖打开大门


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

  • 首先也是最重要的一点是,教育您的同事,如果他们想要一个
    DeviceX
    ,那么他们可以从
    读卡器
    获得它,而不是以其他方式。确保在代码审查中强制执行此操作。 所有其他步骤都只是控制损坏
  • 确保只有
    BaseDevice
    类暴露于读取器实现之外的代码
  • 保护所有设备类的析构函数。这确保了设备类只能由派生类或好友清理(并自动排除非好友的堆栈分配)。如果有人不小心试图直接使用设备类,应该让他们三思而后行
  • 使
    ReaderBase
    成为
    DeviceBase
    的朋友,并为
    ReaderBase
    提供一个函数来实际清理设备。这是必要的,以确保设备可以清理

  • 为什么要担心像
    DeviceBase
    这样的纯抽象基类的可构造性呢?如果它是正确设计的契约或抽象基类,则无论如何都不能构造它。除非你必须适应某种你没有提到的框架,否则只需做与隐藏相反的事情,例如:

    struct DeviceBase {
        virtual void Foo() = 0;
        virtual void Bar() = 0;
        virtual ~DeviceBase() = default;
    };
    
    顺便说一下,声明构造函数或析构函数
    private
    将非常有效地使类“密封”。如果由于某种原因,
    DeviceBase
    不是抽象的(在我看来这是一个严重的设计缺陷),那么就让构造函数
    受到保护
    而不是
    私有
    。需要麻烦的是具体
    设备
    类的构造函数可访问性。假设您要“发布”这些实现类(即它们的定义可供库的用户访问),并且您希望强调禁止直接构造,请使用“访问习惯用法”(我为此发明的名称):


    长话短说,最好的解决办法是根本不发布具体的实现类,其次是限制建设的某种“心理障碍”,例如上述类型的

    别以为你能像朋友一样找到方法。是的,我知道,但在这些模式中,我所需要的只是“创建实例”——而不是接触他们的“私有成员”,我认为当我将基类构造函数设置为私有时,除了朋友之外,我不允许任何人创建实例;由于它是基类的私有构造函数,派生类也不能实例化。。。还是我对这个假设不正确?你可以试试,我其实不太确定。非常感谢!我想这一次我会偏离形式化表示,让一些东西起作用:)现在想想,你实际上暗示,如果我让ReaderBase成为朋友,并保护析构函数(为什么不保护构造函数),我就可以实现我想要的!因此,只要PollforDevice是ReaderBase中的一个虚拟声明函数,它可以进行清理和实例化,我就可以这样做,对吗?@AhmetIpkin:使构造函数受到保护不会增加任何内容,因为这样您就需要再次声明工厂类是它们可以创建的所有内容的朋友。对于析构函数,你没有这个问题,
    struct DeviceBase {
        virtual void Foo() = 0;
        virtual void Bar() = 0;
        virtual ~DeviceBase() = default;
    };
    
    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;
            }
        };
    };
    
    // Your ExternalReader::PollForDevice...
    switch (dt) {
        case Device1:
            return impl_detail::DeviceAccess::Create<ConcreteDevice1>();
        case Device2: 
            // etc...
    }