c++ class 设计、基础 class 继承或外观设计模式

c++ class design, base class inheritance, or facade design pattern

我有一个愚蠢的 C++ 设计问题。有没有办法让一个 class 与多个 class 中的方法具有相同的方法名称(因此,相同的 API)?

我目前的情况是我有classes

struct A
{
    void foo() { std::cout << "A::foo" << std::endl;}
    void boo() { std::cout << "A::boo" << std::endl;}
};

struct B
{
    void moo() { std::cout << "B::moo" << std::endl;}
    void goo() { std::cout << "A::goo" << std::endl;}
};
.... imagine possibly more

我真正想要的是另一个 class 作为这些功能的接口。我可能会误解为 外观设计模式 一个简单的接口,它隐藏了上面实例化 classes 的复杂性,但仍然使用它们相同的接口。

struct C 
{
    void foo() { ... }
    void boo() { ... }
    void moo() { ... }
    void goo() { ... }
};

对于上面显示的少数方法,这可以通过声明结构 A 和 B 或将它们作为参数传递给结构 C 并在 C 中调用 A 和 B 的方法来实现,但如果 A 有 40 个方法,则这是不切实际的B有30个方法。在 C 中重新声明 70 个同名方法来调用 A 和 B 的底层方法,如果我能做得更好,这似乎是无缘无故的大量冗余。

我想到了第二种使用base的方案class

struct base
{
    void foo() { }
    void boo() { }

    void moo() { }
    void goo() { }
};

struct A : public base
{
    void foo() { std::cout << "A::foo" << std::endl;}
    void boo() { std::cout << "A::boo" << std::endl;}
};

struct B : public base
{
    void moo() { std::cout << "B::moo" << std::endl;}
    void goo() { std::cout << "A::goo" << std::endl;}
};

尝试使用具有所有函数定义的 shared_ptr。例如

std::shared_ptr<base> l_var;
l_var->foo();
l_var->boo();
l_var->moo();
l_var->goo();

这仍然不能完全满足我的要求,因为一半的方法是在结构 A 中定义的,而另一半是在结构 B 中定义的。

我想知道多重继承是否可以解决问题,但在学校我听说进行多重继承是不好的做法(调试很难?)

有什么想法或建议吗?基本上它更容易管理结构 A 和 B(等等,因为它是自己的 class 用于抽象目的)。但是希望在某种包装器中以某种方式调用他们的方法的灵活性,其中这种复杂性对用户隐藏。

使用虚拟多重继承

的原因

it's bad practice to do multiple inheritance

是因为直接会导致模棱两可的调用或者冗余数据,所以可以用virtual inheritance来避免

学习 C++ 如何实现 iostream 我想会有很大帮助。

一个Bridge Design Pattern会在这里大放异彩。通过将抽象与其实现分离,许多派生 类 可以单独使用这些实现。

    struct base {    
    protected:
        struct impl;
        unique_ptr<impl> _impl;
    };

    struct base::impl {
        void foo() {}
        void bar() {}
    };    

    struct A :public base {
        void foo() { _impl->foo(); }
    };

    struct B:public base {
        void foo() { _impl->foo(); }
        void bar() { _impl->bar(); }
    };

已编辑(例如实施)

#include <memory>
#include <iostream>


using namespace std;

struct base {
    base();
protected:
    struct impl;
    unique_ptr<impl> _impl;
};

struct base::impl {
    void foo() { cout << " foo\n"; }
    void bar() { cout << " bar\n"; }
    void moo() { cout << " moo\n"; }
    void goo() { cout << " goo\n"; }
};

base::base():_impl(new impl()) {}

struct A :public base {
    A():base() { }
    void foo() { _impl->foo(); }
};

struct B:public base {
    B() :base() { }
    void foo() { _impl->foo(); }
    void bar() { _impl->bar(); }
};


struct C :public base {
    C() :base() { }
    void foo() { _impl->foo(); }
    void bar() { _impl->bar(); }
    void moo() { _impl->moo(); }
    void goo() { _impl->goo(); }
};


int main()
{
    B b;
    b.foo();
    C c1;
    c1.foo();
    c1.bar();   
    c1.moo();
    c1.goo();
    return 0;
}

我认为

Redeclaring 70 methods with the same name in C to call the underlying methods of A and B

正确的道路。

在这种情况下很容易使用多重继承来避免编写传递代码,但我认为这通常是一个错误。更喜欢组合而不是继承。

我会质疑您的用户是否真的想用 70 个方法处理一个接口,但如果那真的是您想要的,那么我不明白为什么 "impractical" 将代码写在 [=11] 中=]:

class C {
    A a;
    B b;
public:
    void foo() { return a.foo(); }
    void boo() { return a.boo(); }
    void moo() { return b.moo(); }
    void goo() { return b.goo(); }
    // ...
};

Live demo.

这样做的好处是,以后可以很容易地改变主意,将AB换成别的东西,而不需要改变C的界面。

您可以通过 using the PIMPL idiom or by splitting CC 的实现进一步隐藏到抽象基础 class C 和实现 CImpl 中.

我支持 Chris Drew 的回答:与组合相比,不仅多重 iharitance 不好,任何 inharitance 都不好。

Fascade 模式的目的是隐藏复杂性。例如,假设您的 类 A 和 B 有 40 种和 30 种方法,Fascade 将公开其中大约 10 种——用户需要的那些。这样就避免了"if A has 40 methods and 30 has methods" then you have a big problem – n.m.

的问题

顺便说一句,我喜欢你使用 struct{} 而不是 class{public:} 的方式。这是颇有争议的,普遍的共识是it constitutes bad form,但stl做到了,我也做到了。

回到问题。如果真的需要公开所有 70 个方法 (!!),我会采用更 pythonistic 的方法:

struct Iface
{
    A _a;
    B _b;
};

如果您设法使字段 const,事情会变得更糟。第三次 - 你可能违反了那些大 类.

的 SRP