创建"Humble Dialogs"视图的方法



关于在创建GUI时将视图与逻辑分离的问题,我有很多问题要回答
下面是一个使用"Humble dialog"方法对一个有标签和按钮的简单对话框所做操作的最小示例。按下按钮应该会在标签上显示一些文本。我用过C++,这是一个我很熟悉的Qt,但我想它对所有其他观众来说都是可读的
在任何情况下,由于语言的选择,我对可能的副作用感兴趣(我在项目中使用C++,我有兴趣介绍它)。

class IView {
public:
    IView(){}
    virtual ~IView(){}
    virtual void showResult(const QString &text)=0;
};
class Presenter {
public:
    Presenter(IView *view){
        m_View = view;
    }
    ~Presenter(){}
    void buttonPressed(){
        QString text;
        // Evaluate text
        m_View->showResult(text);        
    }
private:
    IView *m_View;
}
// Multiple inheritance. Is this OK?
class MyView : public QDialog, public IView {
public:
    MyView(){
        m_Presenter = new Presenter(this);
        m_Button = new QPushbutton(this);
        m_Label = new QLabel(this);
        // Ui event handled inside view but then directly
        // propagated to the Presenter
        connect(m_Button,SIGNAL(clicked()),this,SLOT(buttonPressed()));
    }
    ~MyView(){
        delete m_Presenter;
        // Qt will automatically delete m_Button and m_Label;
    }
    void showResult(const QString &text){
        m_Label->setText(text);
    }
protected slots:
    void buttonPressed(){
        m_Presenter->buttonPressed();
    }
private:
    Presenter *m_Presenter;
    QPushbutton *m_Button;
    QLabel *m_Label;
}
class TestView : public IView {
public:
    TestView(){}
    ~TestView(){}
    void showResult(const QString &text){
        m_LabelText = text;
    }
    QString getResult(){
        return m_LabelText;
    }
private:
    QString m_LabelText;
}
// Test code
TestView view;
Presenter presenter(&view);
presenter.buttonPressed();
EXPECT_EQ(view.getResult(),"Expected Result");
// Procuction code
MyView view;
view.show();

现在,这是我通过跟随费瑟关于谦逊对话的最初工作而得到的。我从Fowler的实现中得到的方法是避免在MyView的构造函数中创建Presenter类的实例,而是将其作为参数传递,这样生产代码看起来就像测试代码。我个人喜欢我在这里介绍的方法。

所以,

  • 它是否意味着要与多重继承一起使用(请参阅我在MyView类中的注释)
  • 事件应该直接传播到演示者,还是应该在将调用相应演示者操作的视图中处理(正如我在这里所做的那样,以避免必须将演示者设置为QObject,从而使其能够处理UI事件)
  • 还有其他意见吗

当您对QObjects进行多重继承时,继承列表中的第一个类需要是QObject派生类。只有当你计划在课堂上添加信号和插槽时,这才是严格要求的,但无论如何,这都是很好的做法。所以你的班级申报:

类MyView:公共IView,公共QDialog{

需要变成这样:

类MyView:公共QDialog,公共IView{

同样,如果你在"MyView"中添加了一个插槽或信号,这只会咬你一口。

除此之外,我认为这是一个很好的实现,尽管对对话来说是一个巨大的过度消耗。:)

我用的是福勒的MVP和Qt,效果不错。事情更容易测试(nUnit风格),但它有点复杂。

我通常在C#Winforms的东西中使用相同的UI模式。

实际上,您并没有真正在这里进行多重继承。您从中继承的一个类只是一个空接口。这里唯一的问题是C++不知道类和接口之间的区别。

我认为这样在视图中创建演示者也没有问题。这不是最可测试的设计,但你可能无论如何都不会测试视图,因为如果你使用的是简陋的对话框,就没有什么可测试的了。或者,您可以通过添加第二个注入演示者的构造函数来实现"穷人的DI",而不是为了测试目的而创建它。

在C#中,我通常有完全不了解演示者的观点,只是抛出事件,而不是给演示者打电话。这增加了一些解耦,但在大多数情况下可能会过于夸张。

总的来说,这是一个很好的实现。如果我必须编写一个带有UI的C++应用程序,我会查看这篇文章

我觉得还可以。但我不会在IView接口中使用QString。如果可能的话,使用一些独立于演示文稿的类型。这样,您就可以在不影响程序逻辑和测试的情况下更改GUI工具包。只有当在QString和std::string或char*之间转换真的很痛苦时,才保留QString(我不知道…)。

相关内容

  • 没有找到相关文章

最新更新