views:

325

answers:

4

I have a bunch of questions to post regarding the issue of separating the view from logic when creating a GUI.
The following is a minimal example of what I would do for a simple dialog that has a label and a button using the "Humble Dialog" approach. Pressing the button should show some text on the label. I have used C++ an Qt that I am comfortable with but I guess it is readable by all other audiences.
In any case I am interested in possible side effects because of the choice of language (I am using C++ in the project I am interested in introducing this).

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();

Now this is what I got by following the initial work on the Humble dialog by Feathers. The approach that I would get from Fowler's implentation would be to avoid creating the instance of the Presenter class in the constructor of MyView but pass it as a parameter instead so the production code would look like the test code. I personally like the approach I present here.

So,

  • Is it meant to be used with multiple inheritance (see my comment in MyView class)?
  • Should the events be propagated directly to the Presenter or should they be handled in the view that will call the respective presenter action (as I have done here to avoid having to make the Presenter a QObject so it can handle UI events)?
  • Are there any other remarks?
A: 

yep....thats ok with multiple inheritance...events can go through the view to the presenter.......

Keith Nicholas
+1  A: 

I usually use the same pattern for my UI in C# Winforms stuff.

You're actually not really doing multiple inheritance here. One of the classes you're inheriting from is just an empty interface. The only problem here is that C++ doesn't know the difference between a class and an interface.

I don't think there's a problem with creating the presenter inside the view like this either. It's not the most testable design but you're probably not going test the view anyway because there's nothing to test there if you're using humble dialog. Or you could do "poor man's DI" by adding a second constructor that injects a presenter instead of creating it for testing purposes.

In C# I usually have views that don't know about the presenter at all and just throw events instead of calling the presenter. This adds some decoupling but might be overkill in most situations.

Overall this is a good implementation. If I'll ever have to write a C++ app with a UI i'm going to check this post

Mendelt
+1  A: 

Looks OK to me. But I would not use QString in the IView-Interface. Use some presentation independend type, if possible. That way, you can change the GUI-toolkit, without affecting the program logic and tests. Keep QString only if it is really painful to convert between QString and std::string or char* (I have no idea...).

EricSchaefer
QString supports Unicode. As long as none of your strings actually have unicode characters, all you pay for the conversion is a bit of performance.
Jan de Vos
+1  A: 

When you do multiple inheritance with QObjects, the first class in the inheritance list needs to be the QObject-derived class. This is only strictly required if you plan to add signals and slots to your class, but is good practice anyway. So your class declaration:

class MyView : public IView , public QDialog {

needs to become this instead:

class MyView : public QDialog , public IView {

Again, this will only bite you if you ever add a slot or signal to "MyView".

Other than that, I think this is a fine implementation, albeit huge amounts of overkill for a dialog. :)

I'm using Fowler's MVP with Qt, and it's working okay. Things are more testable (nUnit style), but it is a bit more complex IMO.

Dave
This is totally correct! I 've been bitten by this. I 'll change the original post.
Yorgos Pagles