构成和继承设计问题

时间:2011-07-26 16:13:12

标签: c++ oop design-patterns

我从一个以抽象类

的形式实现算法的库中获得了这些虚拟类
class A{
    public : 
    virtual void foo() = 0 ;
};


class B{
    public:
        void setA(A * a) { m_a = a ;}

        virtual void f() = 0;
        void g() {m_a->foo();}
    protected:
        A * m_a ;
 };

要使用该库,您只需派生类并实现纯虚函数,如foo(),并提供特定于实现的其他方法(bar())。

class dA : public A {
    public :
         void foo() {/* ... */}
         void bar() {/* ... */}
};

class dB : public B {
     public :
          void f() ;
};

我通常会通过调用

来使用这些类
dB * mydB = new dB() ;
mydB->setA(new dA() );
mydB->f() ;
mydB->g() ;

但是在实施dB::f()时我遇到了设计问题,因为我需要调用dA::bar()特有的dB。但是在课堂上,我只通过B *来保持对dB的引用。然后我想到了两个选择:

  • 每次调用dynamic_cast时使用f()B::m_a投射到dB*
  • 将m_dA成员添加到dB,该成员存储与m_a相同的指针,但可用于访问dB特定函数。

当然我不能改变基类。

我想知道这个问题是否有更优雅的解决方案(就像我没想到的设计模式)。如果没有,我应该选择哪一个?

3 个答案:

答案 0 :(得分:2)

你有第三个解决方案。在setA()中添加功能dB。当然,如果B::setA()被实现为:{/ p>,此功能将隐藏dB::setA(),这对您有好处。

class dB : public B 
{
     dA *m_dA ; //add this member also!
     public :
          void f()
          {
             m_dA->bar(); //fast forward : no cast here!
          }
          void setA(A *a) //this hides B::setA()
          {
             m_dA= dynamic_cast<dA*>(a); //just one time dynamic cast!
             if ( m_dA == 0 )
             {  
                 throw std::runtime_error("invalid argument");
             }
             B::setA(a); //now call the hidden function in the base!
          }
};

这样,每次拨打dB::f()时都不需要dynamic_cast,这样可以快速拨打电话!

答案 1 :(得分:1)

dB作为具体类不应该在dA上调用不相关的具体类的方法。即使使用reinterpret_cast,这也是糟糕的设计,无法将不相关的物体耦合在一起。常用功能应放在公共基类或接口中。

例如,类A可以被视为一个接口,因为它只有纯虚方法。因此,如果您想在dB上使用该接口,那么使用多重继承是安全的。在这种情况下,您当然必须实现自己的foo

class dB : public B, public A {
     public :
          void f();
          void foo();
};

如果bar()是您想要的那个,并且由于某种原因您可以更改接口A,然后创建自己的接口,提供纯虚函数bar(),然后同时创建dA dB 1}}和bar()从您的新接口继承并相应地实现dA并使用接口指针。

如果你必须在另一个方面使用一个,那么组合是要走的路,而不是可能使动态转换失败的模糊基指针。在dB内建立一个具体的{{1}}成员。

答案 2 :(得分:0)

只有当它具有的A实际上是dA时,你的dB才能工作。你需要确保始终如此。

因此,覆盖setA方法以使用dynamic_cast来检查它是否真的是dA。现在,您是否在那时将结果保存在m_dA或稍后再次进行动态转换是不重要的。初始化dB的可能性较小,其动态转换可能会在以后失败。