【问题标题】:Is multiple inheritance a good design pattern in my case?就我而言,多重继承是一种好的设计模式吗?
【发布时间】:2017-10-18 10:36:18
【问题描述】:

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

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

  • A继承WindowInteractingWindowCoordinateAxis
  • B继承WindowWithSettingsDialogWindowCoordinateAxis

因此,通过互联网阅读我现在到处都可以看到(例如 Google 样式指南),只有当 WindowWindowInteractingWindowCoordinateAxisWindowWithSettingsDialog 都是纯抽象类/接口时,我才应该这样做.

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

这种“菱形继承”应该是经常出现的问题,请问有什么方法可以解决呢?

【问题讨论】:

  • 我不会将窗口用作具有坐标轴的窗口的基类,而是使用合成。只要您想在同一个窗口中拥有 2 个坐标轴,您的设计就会变得复杂(而原则上它应该很容易)
  • IMO 它不会只保留 WindowWithAxisWindowInteractingWindowWithSettingsDialog。你很快就会拥有WindowWithButtonWindowWith2AxisWindowWithScrollbar,你的类将是一团不可改变的代码,因为一切都与一切相关,如果没有完全重新设计,就不可能做任何事情。坚持只使用Window 并创建一个Axis 这是一个Window 并创建它以便您可以将Windows 添加到其他Windows。或者只是将 Qt 与 QWT 一起使用,它已经为您解决了所有这些问题。
  • @Rosme 在引用其他网站时,指出cross-posting is frowned upon 通常会有所帮助
  • 人们警告不要使用多重继承的真正原因是,这种方法通过继承它需要做的所有事情来拼凑一个全能对象,如果它很难维护,并且使得重用代码更加困难从长远来看。

标签: c++ oop inheritance design-patterns


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我想我会采用以下方式:

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

    从中派生一个 WindowImpl,它以要素类列表为模板。每个功能都可以选择是否响应事件。

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

    例如:

    #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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-24
      • 1970-01-01
      • 2017-02-11
      • 2019-07-20
      • 1970-01-01
      • 1970-01-01
      • 2014-04-15
      相关资源
      最近更新 更多