2008-10-27 4 views
4

GUI를 만들 때 논리와 뷰를 분리하는 문제와 관련하여 게시 할 질문이 많습니다.
다음은 "험블 대화 상자"접근 방식을 사용하는 레이블과 버튼이있는 간단한 대화 상자에 대해 수행 할 작업의 최소 예입니다. 버튼을 누르면 레이블에 텍스트가 표시됩니다. 저는 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(); 

이제는 initial work on the Humble dialog by Feathers을 따라갔습니다. Fowler's implentation에서 얻을 수있는 접근 방법은 MyView 생성자에서 Presenter 클래스의 인스턴스를 만드는 것을 피하고 대신 프로덕션 코드가 테스트 코드처럼 보이도록 매개 변수로 전달하는 것입니다. 나는 개인적으로 내가 여기 제시 한 접근 방식을 좋아한다.

그래서,

  • 는 다중 상속 (MYVIEW 클래스 내 댓글 참조)와 함께 사용하기위한 것입니까?
  • 이벤트를 발표자에게 직접 전달해야합니까? (발표자를 QObject로 만들어서 UI 이벤트를 처리 할 필요가 없도록하기 위해 여기에서 수행 한 것처럼) 각각의 발표자 동작을 호출하는보기에서 이벤트를 처리해야합니까?
  • 다른 의견이 있으십니까?

답변

1

저는 보통 C# Winforms의 UI에서 같은 패턴을 사용합니다.

실제로 여기에 다중 상속을 실제로하지는 않습니다. 상속 받고있는 클래스 중 하나는 비어있는 인터페이스입니다. 여기서 유일한 문제는 C++이 클래스와 인터페이스의 차이점을 알지 못한다는 것입니다.

이와 같이보기 내부에 발표자를 만드는 데 문제가 있다고 생각하지 않습니다. 가장 테스트할만한 디자인은 아니지만 어쨌든 겸손한 대화를 사용하는 경우에는 테스트 할 것이 없기 때문에 아마 뷰를 테스트하지 않을 것입니다. 또는 테스트 목적으로 생성자를 생성하는 대신 발표자를 삽입하는 두 번째 생성자를 추가하여 "가난한 사람의 DI"를 수행 할 수 있습니다.

C#에서는 대개 발표자에 대해 알지 못하고 발표자에게 전화하는 대신 이벤트를 전달하는보기가 있습니다. 이것은 일부 디커플링을 추가하지만 대부분의 상황에서 지나치게 길 수도 있습니다.

전체적으로 이것은 좋은 구현입니다. UI가있는 C++ 응용 프로그램을 작성해야한다면이 게시물을 확인하겠습니다.

1

나에게 잘 보입니다. 하지만 IView-Interface에서는 QString을 사용하지 않습니다. 가능한 경우 프리젠 테이션 독립형 형식을 사용하십시오. 이렇게하면 프로그램 논리 및 테스트에 영향을주지 않고 GUI 툴킷을 변경할 수 있습니다. QString을 QString과 std :: string 또는 char * 사이에서 변환하는 것이 정말 고통 스러울 경우에만 유지하십시오 (전혀 모릅니다 ...).

+0

QString은 유니 코드를 지원합니다. 문자열에 실제로 유니 코드 문자가없는 한 변환에 대해 지불하는 것은 약간의 성능입니다. –

2

QObject로 다중 상속을 수행하는 경우 상속 목록의 첫 번째 클래스는 QObject 파생 클래스 여야합니다. 신호 및 슬롯을 수업에 추가하려는 경우에만 엄격하게 요구되지만, 어쨌든 우수 사례입니다.그래서 클래스 선언 :

클래스 MYVIEW : 공공 개의 IView, 공공 인 QDialog는 {

요구 대신이되기 위해 :

클래스 MYVIEW : 공공 인 QDialog, 공공 개의 IView {

다시 말하지만, 이것은 단지 것 "MyView"에 슬롯 또는 신호를 추가하는 경우 귀하를 물으십시오.

이 외에도 대화 상자에 엄청난 잔인 함이 있지만 훌륭한 구현이라고 생각합니다. :)

Qow와 함께 Fowler의 MVP를 사용하고 있습니다. 정상적으로 작동합니다. 상황을 더 테스트 할 수 있지만 (nUnit 스타일), 좀 더 복잡한 IMO입니다.

+0

이것은 완전히 맞습니다! 나는 이것에 물렸다. 원래 게시물을 변경하겠습니다. –

관련 문제