【问题标题】:c++ design: reducing coupling in a deep class hierarchyC++设计:减少深层类层次结构中的耦合
【发布时间】:2013-11-22 09:15:00
【问题描述】:

请考虑这个类层次结构:

#include <iostream>

struct Base
{    
    Base(int arg1, int arg2, int arg3, int arg4)
    {
        std::cout << "Base::Base:" << arg1 << "," << arg2 << "," << arg3 << "," << arg4 << std::endl;
    }
    void BaseDoWork()
    {
        std::cout << "Base::BaseDoWork" << std::endl;
    }
};

struct Derived1:public Base
{
    Derived1(int arg1, int arg2, int arg3, int arg4):Base(arg1, arg2, arg3, arg4){}
    void DerivedDoWork()
    {
        BaseDoWork();
    }
};

struct Derived2:public Derived1
{
    Derived2(int arg1, int arg2, int arg3, int arg4):Derived1(arg1, arg2, arg3, arg4){}
};

int main(int argc, char *argv[])
{
    Derived2 myDerived2(1,2,3,4);
    myDerived2.DerivedDoWork();
    return 0;
}

Derived1 类的耦合比我想的要多:它必须知道如何构造“Base”类。在较深的层次结构中,这意味着将“基”类构造函数的参数一直沿类层次结构向下传播。更改“基”类构造函数需要更改所有派生类。

我对这个类层次结构的目标是:

  1. Derived1 需要基类中的 BaseDoWork() 方法,但不需要知道如何构造基类。

  2. Derived2 知道如何构造“Base”类。

理想情况下,我想让 Derived1 成为接受任何具有可访问“BaseDoWork()”方法的基类的模板,如下所示:

template <T> struct Derived1:public T
{
    void DerivedDoWork()
    {
        BaseDoWork();
    }
};

但是,如果我将上述模板实例化为Derived1&lt;Base&gt;,如果不向 Derived1 添加“Base”构造函数参数的知识,我将看不到如何构造“Base”类。

【问题讨论】:

    标签: c++ coupling


    【解决方案1】:

    假设这是真正的层次结构,如果Derived1Base 派生类,则必须知道如何构造父类(Base)。

    据我了解,也许我错了,如果Derived1 不是真正的Base 派生(子)类,只需要知道它的接口,就不需要知道如何构造@987654326 @ 实例。所以你可以定义 Derived1 如下:

    struct Derived1
    {
        Derived1(int arg1, int arg2, int arg3, int arg4)
        {}
    
        .....
    };
    

    只有Base派生类需要知道如何构造Base

    struct Derived2 : public Base
    {
        Derived2(int arg1, int arg2, int arg3, int arg4) : 
            Base(arg1, arg2, arg3, arg4)
        {}
    
        .....
    };
    

    现在,如果Derived2 真的是Derived1 孩子,必须知道如何创建它的父母。之前的代码变成了:

    struct Derived2 : public Base, public Derived1
    {
        Derived2(int arg1, int arg2, int arg3, int arg4) : 
            Base(arg1, arg2, arg3, arg4),
            Derived1(arg1, arg2, arg3, arg4)
        {}
    
        .....
    };
    

    如果你必须使用Derived1中的Base::BaseDoWork()方法,你可以定义类如下:

    struct Base
    {    
        Base(int arg1, int arg2, int arg3, int arg4)
        {
            std::cout << "Base::Base:" << arg1 << "," << arg2 << "," << arg3 << "," << arg4 << std::endl;
        }
    
        virtual void BaseDoWork()
        {
            std::cout << "Base::BaseDoWork" << std::endl;
        }
    };
    
    struct Derived1
    {
        Derived1(int arg1, int arg2, int arg3, int arg4)
        {}
    
        template <typename T>
        void DerivedDoWork(T & base)  // only knows T interface
        {
          base.BaseDoWork();
        }
    };
    
    struct Derived2 : public Base, public Derived1
    {
        Derived2(int arg1, int arg2, int arg3, int arg4) : 
          Base(arg1, arg2, arg3, arg4),
          Derived1(arg1, arg2, arg3, arg4)
        {}
    
        virtual void DerivedDoWork()
        {
          Derived1::DerivedDoWork<Base>(*(static_cast<Base *>(this)));
        }
    };
    

    主要保持不变。

    您可能需要查看此层次结构。

    【讨论】:

    • 是的,这可能是最简单的解决方案。我从 Derived1 构造函数中删除了参数列表,因为从这个参数列表中解耦 Derived1 是主要目标之一。并且 DoWork() 方法不需要是虚拟的。
    【解决方案2】:

    当你让publicBase继承Derived1时,结果是Derived1已经知道如何构造Base,所以如果你想要Derived1不知道如何构造Base ,也许最好不要从Base继承它?相反,将Base 分隔为某种表演者。

    考虑一下这段代码,我并不认为这是解决方案,但也许它会让你产生其他想法(类的名称相同):

    #include <iostream>
    
    struct Base
    {
        Base()
        {
            std::cout << "Base::Base:" << std::endl;
        }
        Base(int arg1, int arg2, int arg3, int arg4)
        {
            std::cout << "Base::Base:" << arg1 << "," << arg2 << "," << arg3 << "," << arg4 << std::endl;
        }
        void DoWork()
        {
            std::cout << "Base::BaseDoWork" << std::endl;
        }
    };
    
    template <typename T>
    struct Derived1
    {
        T object;
    
        Derived1(T obj){ object = obj; }
        void DerivedDoWork()
        {
            object.DoWork();
        }
    };
    
    template <typename T>
    struct Derived2 : public Derived1<T>
    {
        Derived2(int arg1, int arg2, int arg3, int arg4) : Derived1( T(arg1, arg2, arg3, arg4))
        { }
    };
    
    int main(int argc, char *argv[])
    {
        Derived2<Base> myDerived2(1,2,3,4);
        myDerived2.DerivedDoWork();
        return 0;
    }
    

    【讨论】:

    • 我没有想到将实际对象存储在 Derived1 中。据我所知,您为 Base 添加的默认构造函数从未被调用:我错过了这个的用途吗?
    • @SimonElliott,Base 的默认构造函数在创建Derived1 对象时调用一次,我只是添加了默认构造函数以使Base 对象成为Derived1 的成员。您可以将T object 替换为T&amp; objectconst T&amp; objectT* objectconst T* object 并在Derived1 构造函数中初始化此引用/指针,因此Base 不会是Derived1 的成员跨度>
    • 有道理。我没有测试自己实例化 Derived1。
    猜你喜欢
    • 2016-04-22
    • 2011-05-19
    • 1970-01-01
    • 2013-11-21
    • 2011-05-07
    • 1970-01-01
    • 1970-01-01
    • 2012-06-18
    • 2014-12-06
    相关资源
    最近更新 更多