策略模式Qt模糊基础

时间:2017-12-09 14:54:03

标签: c++ qt strategy-pattern ambiguous

对于一个学校项目,我们必须制作一个“游戏”,在这个游戏中,我们想象一个迷宫并找到我们的方式通过它。这种可视化必须以两种不同的方式实现,在我们的例子中是基于文本的可视化和更奇特的方式。

我们必须使用Qt,我们正在使用带有QWidget的QMainWindow,这个小部件将是一个或另一个可视化。看到在游戏过程中我们应该能够在可视化之间切换,我们使用策略模式,因此创建了一个接口(ViewInterface),两个可视化实现了这一点。除了实现ViewInterface之外,两个可视化都继承了另一个类,在基于文本的情况下,这是QPlainTextEdit(具有带文本的QWidget),而在Fancy中,这是QDialog。这里的探针是在我的控制器中,我有一个指向ViewInterface的指针,用于填充QWidet但是这样做ViewInterface也必须从QWidget继承,这会导致这个错误:QObject是'TerminalView'的模糊基础。 / p>

由于视图之间的切换可以在玩游戏时完成,并且只应在当前活动视图上调用更新,因此我们无法将特定视图传递给'setWidget'。

here is a visualization of the inheritance structure:

我做错了什么或者如何解决这个问题? (我已经考虑过但不能提供解决方案)。

2 个答案:

答案 0 :(得分:1)

这里的问题是你继承QObject两次:首先是在ViewInterface层次结构中,第二个是QDialog层次结构(diamond problem)。 尝试为FancyView和TextView类使用虚拟继承不工作,因为虚拟继承应该在完整的层次结构中使用

但是你的设计存在另一个问题:QDialog和QPlainTextEdit都继承了QWidget。要解决此问题,您可以执行以下操作:

  1. 在不继承QObject的情况下创建ViewInterface摘要。这个抽象类将定义你的FancyView和TextView的接口,可能会也可能不会实现一些常见的逻辑。

  2. 使用QDialog和ViewInterface以及QPlainTextEdit和ViewInterface的多重继承实现FancyView和TextView。

  3. 这样你可能不会遇到任何模糊基类的问题。

    更新:

      

    还没有看到你的编辑:确实这会解决问题,但是   另一个问题是:如果我这样做,我就不能使用ViewInterface   指针设置我的QWidget。确实可能,但在我看来   这不是很干净

    嗯,这是一个真正的问题。一个明显的解决方案是不使用ViewInterface*而不是QWidget*。但这意味着您需要更改相当多的代码,在您的情况下实际上可能并不那么好。

    关于给定的评论,我提出了另一种解决方案:

    1. ViewInterface继承QWidget(包含所有需要的界面功能):

      class ViewInterface: public QWidget {
          Q_OBJECT
          ...
      }
      
    2. 在小部件使用的ViewInterface构造函数集布局中进行设置:

      ViewInterface::ViewInterface (QWidget* i_parent)
          : QWidget (i_parent) {
          auto layout {new QGridLayout (this)};
      
          // Zeroed margins to make full fit.
          layout->setContentsMargins (0, 0, 0, 0);
      
          setLayout (layout);
      }
      
    3. 在派生类的构造函数中,将特定小部件添加到布局:

      class FancyView : public ViewInterface {
          Q_OBJECT
      
          FancyView (QWidget* i_parent) 
              : ViewInterface (i_parent)
              , dialog_widget_ {new QDialog (this)} {
              layout->addWidget (dialog_widget_);
          }
          ...
      
      private:
          QDialog* dialog_widget_;
      }
      
    4. 使用目标小部件实现所需的界面。如果您要处理事件,可以使用QObject::eventFilter ()。在这种情况下,您应该将上面代码中的FancyView对象设置为dialog_widget_的事件过滤器。

    5. 注意:在这种情况下,您无法将FancyView用作QDialog。此问题的一个工作原理是代理QDialog信号,插槽和公共函数,并创建另一个包装类FancyViewDialog,它作为FancyView中方法,信号和插槽的代理。这不是一个很棒的解决方案,但我没有看到任何其他解决钻石问题的方法,它允许ViewInterfaceQWidget之间存在“is-a”关系。

答案 1 :(得分:0)

接口应该是虚拟方法的抽象,并且没有具体的基类。 ViewInterface不应继承QWidget。这解决了你的问题。

现在至少有两种解决方案可以将ViewInterface实例转换为QWidget

  1. 模板ViewInterface的用户并让他们确保所使用的具体类型实际上来自QWidget。如果类型没有运行时多态性,那将会有效。

  2. 如果存在运行时多态性,请在接口中添加QWidget * widget() = 0方法,并在派生方法中实现它。这很简单:QWidget * widget() override { return this; }

  3. 接口可以同时具有信号和插槽 - 它们必须是虚拟方法,但它们肯定会起作用。参见例如this answer开始使用虚拟信号。

    如果要在ViewInterface的两个具体实现之间共享一些代码,则可以使用派生自ViewInterface的其他类来提供共享代码。 TerminalViewFancyView都来自该类。可以在基类类型上对助手类进行参数化,以使其跳过较少的箍来访问小部件,例如:class TerminalView : ViewHelper<QPlainTextEdit> { ... };