繁体   English   中英

在我的情况下,多重继承是一个好的设计模式吗?

[英]Is multiple inheritance a good design pattern in my case?

我有以下问题:我希望有多个Windows都显示某种功能图。 假设一个窗口应显示坐标轴并具有一些交互功能,另一个窗口还应显示坐标轴并具有设置对话框。

我是如何直观地实现这一点的,是将Window实现为一些用我的框架(在我的案例中为VTK)初始化基本窗口的类,一些虚拟继承类WindowWithAxisWindowInteractingWindowWithSettingsDialog然后有

  • A类继承WindowInteractingWindowCoordinateAxis
  • B类继承WindowWithSettingsDialogWindowCoordinateAxis

因此,通过互联网阅读我现在到处都可以看到(例如Google风格指南),如果WindowWindowInteractingWindowCoordinateAxisWindowWithSettingsDialog都是纯抽象类/接口,我应该这样做。

这是我不明白的。 这是不是意味着我不被允许实际实现这些类,我将不得不在A类和B类(以及使用这些类的所有其他类)中实现WindowWindowCoordinateAxis所有功能? 这对我来说似乎不是一个干净的解决方案。

这种“菱形继承”应该是一个经常出现的问题,那么我可以采用哪种方式来解决这个问题呢?

一般原则是避免代码/功能重复并提高可重用性。 在您的情况下, WindowInteractingWindowCoordinateAxis类都具有相同的功能(来自一个Window)。 相反,要么定义一个名为IInteractingIHasTheCoordinateAxis的接口(具有纯虚函数的类),并提供它们的具体实现,或者根据传递的参数使您的Window类行为不同。

我想我会采用以下方式:

定义多态Window基类,它是处理UI事件的接口。

从中导出一个WindowImpl,使用一个Feature类列表进行模板化。 每个功能都可以选择是否响应事件。

WindowImpl通过枚举一组特征来调用每个Feature对象上的事件代码。 功能可能会保持自己的状态。

例如:

#include <utility>
#include <tuple>
#include <iostream>

/// Basic concept of a window that handles events
struct Window
{
    Window(std::string title)
        : title_(std::move(title)) {}

    // all windows handle clicks, but defer to a private polymorphic implementation
    void onClickOrWhatever()
    {
        handleOnClickOrWhatever();
    }

    // all windows have a title
    auto title() const -> const std::string&
    {
        return title_;
    }

private:
    // make ploymorphism an implementation detail
    virtual void handleOnClickOrWhatever() = 0;

    std::string title_;
};

// All concrete windows are a WindowImpl customised with a feature list
template<class...Features>
struct WindowImpl
    : Window
{
    static constexpr auto FeatureCount = sizeof...(Features);

    using Window::Window;

private:

    // the onClick handler defers to actions specified by each enabled feature
    void handleOnClickOrWhatever() override
    {
        std::cout << title() << " detects a click (or whatever)\n";
        implHandleOnClickOrWhatever(features_,
                                    std::make_index_sequence<FeatureCount>());
        std::cout << std::endl;
    }

    template<class FeatureTuple, std::size_t...Is>
    void implHandleOnClickOrWhatever(FeatureTuple&& features, std::index_sequence<Is...>)
    {
        using expand = int[];
        void(expand{0,
                    (std::get<Is>(features).handleClick(*this), 0)...
        });

    }


    using FeatureTuple = std::tuple<Features...>;
    FeatureTuple features_;
};



// a base feature that takes no action
struct BaseFeature
{
    template<class Window>
    static void handleClick(Window& window)
    {
        // don't respond
    }
};

//
// some features
//

struct HasAxis
    : BaseFeature
{
    template<class Window>
    static void handleClick(Window& window)
    {
        std::cout << "testing touch on axis" << std::endl;
    }
};

struct HasSettingsDialog
    : BaseFeature
{
    template<class Window>
    static void handleClick(Window& window)
    {
        std::cout << "testing touch on settings button" << std::endl;
    }
};

struct HasAlphaBackground
    : BaseFeature
{
    // note : does not handle clicks
};

// some customised windows, mixing anf matching features
using WindowWithAxis = WindowImpl<HasAxis>;
using WindowWithAxisAndSettings = WindowImpl<HasAxis, HasSettingsDialog>;
using WindowWithAxisAndAlpha = WindowImpl<HasAxis, HasAlphaBackground>;

int main()
{
    WindowWithAxis w{"window 1"};
    w.onClickOrWhatever();

    WindowWithAxisAndSettings w2{"window 2"};
    w2.onClickOrWhatever();

    WindowWithAxisAndAlpha w3{"window 3"};
    w3.onClickOrWhatever();
}

预期产量:

window 1 detects a click (or whatever)
testing touch on axis

window 2 detects a click (or whatever)
testing touch on axis
testing touch on settings button

window 3 detects a click (or whatever)
testing touch on axis

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM