WinForms의 Model-View-Presenter


90

WinForms를 사용하여 MVP 방법을 처음으로 구현하려고합니다.

각 레이어의 기능을 이해하려고 노력하고 있습니다.

내 프로그램에는 클릭하면 openfiledialog 창을 여는 GUI 버튼이 있습니다.

따라서 MVP를 사용하여 GUI는 버튼 클릭 이벤트를 처리 한 다음 presenter.openfile ();

presenter.openfile () 내에서 해당 파일의 열기를 모델 계층에 위임해야합니까, 아니면 처리 할 데이터 나 논리가 없기 때문에 단순히 요청에 따라 작동하고 openfiledialog 창을 열어야합니까?

업데이트 : 이에 대한 추가 지원이 필요하다고 생각되는 현상금을 제공하기로 결정했으며, 상황에 맞게 아래의 특정 사항에 맞게 조정하는 것이 바람직합니다.

좋습니다. MVP를 읽은 후 Passive View를 구현하기로 결정했습니다. 효과적으로 나는 발표자가 처리 할 Winform에 대한 많은 컨트롤과 모델 (들)에 위임 된 작업을 갖게 될 것입니다. 내 구체적인 요점은 다음과 같습니다.

  1. winform이로드되면 트 리뷰를 얻어야합니다. 따라서 뷰가 다음과 같은 메소드를 호출해야한다고 생각하는 것이 맞습니까? 발표자, 그러면 뷰로 전달되어 패널에 할당 될까요?

  2. DataGridview도 있으므로 Winform의 모든 데이터 컨트롤에 대해 동일합니까?

  3. 내 앱에는 동일한 어셈블리를 가진 여러 모델 클래스가 있습니다. 또한 시작시로드해야하는 플러그인이있는 플러그인 아키텍처를 지원합니다. 뷰가 단순히 프레젠터 메서드를 호출하면 플러그인을로드하고 뷰에 정보를 표시하는 메서드가 호출됩니까? 그런 다음 플러그인 참조를 제어하는 ​​계층입니다. 보기가 그들 또는 발표자에 대한 참조를 보유합니까?

  4. 뷰가 트 리뷰 노드 색상에서 데이터 그리드 크기 등에 이르기까지 프레젠테이션에 대한 모든 것을 처리해야한다고 생각하는 것이 맞습니까?

나는 그것이 나의 주요 관심사라고 생각하고 이러한 흐름이 어떻게되어야하는지 이해하면 괜찮을 것이라고 생각합니다.


이 링크 lostechies.com/derekgreer/2008/11/23/… 은 MVP 스타일 중 일부를 설명합니다. Johann의 탁월한 답변 외에도 도움이 될 수 있습니다.
ak3nat0n

답변:


123

MVP와 특정 문제에 대한 저의 겸손한 견해입니다.

첫째 , 사용자가 상호 작용하거나 표시 할 수있는 모든 것이 입니다. 이러한 뷰의 법칙, 행동 및 특성은 인터페이스 로 설명됩니다 . 해당 인터페이스는 WinForms UI, 콘솔 UI, 웹 UI를 사용하여 구현할 수 있으며 UI를 전혀 사용하지 않을 수도 있습니다 (일반적으로 발표자를 테스트 할 때). 구체적인 구현은 뷰 인터페이스의 법칙을 준수하는 한 중요하지 않습니다. .

둘째 ,보기는 항상 발표자가 제어합니다 . 그러한 발표자의 법칙, 행동 및 특성도 인터페이스로 설명됩니다 . 해당 인터페이스는 뷰 인터페이스의 법칙을 준수하는 한 구체적인 뷰 구현에 관심이 없습니다.

셋째 , 발표자가보기를 제어하기 때문에 종속성을 최소화하기 위해보기가 발표자에 대해 아는 것이 전혀 없습니다. 발표자와보기간에 합의 된 계약이 있으며 이는보기 인터페이스에 의해 명시됩니다.

Third 의 의미 는 다음과 같습니다.

  • 발표자는보기에서 호출 할 수있는 메서드가 없지만보기에는 발표자가 구독 할 수있는 이벤트가 있습니다.
  • 발표자는 자신의 견해를 알고 있습니다. 구체적인 발표자에 생성자 주입을 사용하여이를 수행하는 것을 선호합니다.
  • 보기는 어떤 발표자가 그것을 제어하고 있는지 전혀 모릅니다. 발표자에게 제공되지 않습니다.

귀하의 문제에 대해 위의 내용은 다소 단순화 된 코드로 표시 될 수 있습니다.

interface IConfigurationView
{
    event EventHandler SelectConfigurationFile;

    void SetConfigurationFile(string fullPath);
    void Show();
}

class ConfigurationView : IConfigurationView
{
    Form form;
    Button selectConfigurationFileButton;
    Label fullPathLabel;

    public event EventHandler SelectConfigurationFile;

    public ConfigurationView()
    {
        // UI initialization.

        this.selectConfigurationFileButton.Click += delegate
        {
            var Handler = this.SelectConfigurationFile;

            if (Handler != null)
            {
                Handler(this, EventArgs.Empty);
            }
        };
    }

    public void SetConfigurationFile(string fullPath)
    {
        this.fullPathLabel.Text = fullPath;
    }

    public void Show()
    {
        this.form.ShowDialog();        
    }
}

interface IConfigurationPresenter
{
    void ShowView();
}

class ConfigurationPresenter : IConfigurationPresenter
{
    Configuration configuration = new Configuration();
    IConfigurationView view;

    public ConfigurationPresenter(IConfigurationView view)
    {
        this.view = view;            
        this.view.SelectConfigurationFile += delegate
        {
            // The ISelectFilePresenter and ISelectFileView behaviors
            // are implicit here, but in a WinForms case, a call to
            // OpenFileDialog wouldn't be too far fetched...
            var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
            selectFilePresenter.ShowView();
            this.configuration.FullPath = selectFilePresenter.FullPath;
            this.view.SetConfigurationFile(this.configuration.FullPath);
        };
    }

    public void ShowView()
    {
        this.view.SetConfigurationFile(this.configuration.FullPath);
        this.view.Show();
    }
}

위의 것 외에도 일반적으로 내 뷰가 일반적으로 혜택을받는 소유자 뷰 또는 뷰 제목을 IView숨기는 기본 인터페이스가 Show()있습니다.

귀하의 질문에 :

1. winform이로드되면 트 리뷰를 얻어야합니다. 따라서 뷰가 다음과 같은 메소드를 호출해야한다고 생각하는 것이 맞습니까? 발표자, 그러면 뷰로 전달되어 패널에 할당 될까요?

나는 부를 것이다 IConfigurationView.SetTreeData(...)에서 IConfigurationPresenter.ShowView()오른쪽으로 호출하기 전에,IConfigurationView.Show()

2. DataGridview도 있으므로 Winform의 모든 데이터 컨트롤에 대해 동일합니까?

네, 전화하겠습니다 IConfigurationView.SetTableData(...). 주어진 데이터의 형식을 지정하는 것은보기에 달려 있습니다. 발표자는 단순히 표 형식의 데이터를 원한다는보기의 계약을 따릅니다.

3. 내 앱에는 동일한 어셈블리를 가진 여러 모델 클래스가 있습니다. 또한 시작시로드해야하는 플러그인이있는 플러그인 아키텍처를 지원합니다. 뷰가 단순히 프레젠터 메서드를 호출하면 플러그인을로드하고 뷰에 정보를 표시하는 메서드가 호출됩니까? 그런 다음 어떤 계층이 플러그인 참조를 제어합니다. 보기가 그들 또는 발표자에 대한 참조를 보유합니까?

플러그인이보기와 관련된 경우보기는 해당 플러그인에 대해 알아야하지만 발표자는 알아야합니다. 모든 것이 데이터와 모델에 관한 것이라면 뷰는 그들과 관련이 없어야합니다.

4. 뷰가 트 리뷰 노드 색상에서 데이터 그리드 크기 등에 이르기까지 프레젠테이션에 대한 모든 것을 처리해야한다고 생각하는 것이 맞습니까?

예. 데이터를 설명하는 XML과 데이터를 가져와 CSS 스타일 시트를 적용하는 뷰를 제공하는 발표자로 생각하십시오. 구체적으로 말하면 발표자가 전화를 걸면 IRoadMapView.SetRoadCondition(RoadCondition.Slippery)뷰가 도로를 빨간색으로 렌더링합니다.

클릭 한 노드의 데이터는 어떻습니까?

5. 트리 노드를 클릭 할 때 특정 노드를 통해 발표자에게 전달한 다음 발표자가 필요한 데이터를 파악한 다음보기에 다시 표시하기 전에 해당 데이터를 모델에 요청해야합니까?

가능하면 뷰에 트리를 한 번에 표시하는 데 필요한 모든 데이터를 전달합니다. 그러나 일부 데이터가 너무 커서 처음부터 전달할 수 없거나 그 특성이 동적이고 모델의 "최신 스냅 샷"이 필요한 경우 (프레젠터를 통해) event LoadNodeDetailsEventHandler LoadNodeDetails보기 인터페이스에 다음과 같은 것을 추가 합니다. 발표자는 그것을 구독하고, LoadNodeDetailsEventArgs.Node모델 에서 (아마도 어떤 종류의 ID를 통해) 노드의 세부 사항을 가져 와서 이벤트 핸들러 델리게이트가 반환 할 때 뷰가 표시된 노드 세부 사항을 업데이트 할 수 있습니다. 좋은 사용자 경험을 위해 데이터를 가져 오는 속도가 너무 느릴 수있는 경우 이러한 비동기 패턴이 필요할 수 있습니다.


3
뷰와 발표자를 반드시 분리해야한다고 생각하지 않습니다. 나는 보통 모델과 발표자를 분리하여 발표자가 모델 이벤트를 듣고 그에 따라 행동하도록합니다 (뷰 업데이트). 보기에 발표자가 있으면보기와 발표자 간의 커뮤니케이션이 쉬워집니다.
kasperhj

11
@lejon : 당신은 말할 뷰에서 발표자를 갖는 것은보기와 발표자 간의 통신을 용이하게 ,하지만 난 강력하게 동의하지 않는다. 내 관점은 이것이다 : 뷰가 프레젠터에 대해 알고있을 때 뷰는 각 뷰 이벤트 에 대해 어떤 프레젠터 메서드 를 호출 할 것인지 결정해야합니다 . 뷰가 어떤 프레젠터 메서드에 해당 하는 뷰 이벤트 를 실제로 알지 못하기 때문에 이것이 "복잡성의 2 점" 입니다. 계약은 그것을 명시하지 않습니다.
Johann Gerell 2011

5
@lejon : 반면에 뷰가 실제 이벤트 만 노출하면 발표자 자신 (뷰 이벤트가 발생할 때 수행 할 작업을 알고있는 사람)이 올바른 작업을 수행하기 위해 구독합니다. 그것은 단지 "복잡도의 1 점"에 불과하며, 제 책에서는 "복잡성의 2 점"보다 두 배나 좋습니다. 일반적으로 결합이 적다는 것은 프로젝트를 진행하는 동안 유지 보수 비용이 적다는 것을 의미합니다.
Johann Gerell 2011

9
역시이 링크 lostechies.com/derekgreer/2008/11/23/… 에 설명 된 캡슐화 된 발표자를 사용하는 경향이 있습니다. 여기에서보기는 발표자의 유일한 소유자입니다.
ak3nat0n

3
@ ak3nat0n : 제공 한 링크에 설명 된 MVP의 세 가지 스타일과 관련하여 Johann의이 답변은 Observing Presenter Style 이라는 세 번째 스타일과 가장 밀접하게 일치 할 것이라고 생각합니다 . "
DavidRR

11

보기의 모든 논리 를 포함하는 발표자 는 @JochemKempe가 말한 것처럼 클릭되는 버튼에 응답해야합니다 . 실제로 버튼 클릭 이벤트 핸들러는 presenter.OpenFile(). 그러면 발표자는 수행해야 할 작업을 결정할 수 있습니다.

사용자가 파일을 선택해야한다고 결정 하면 뷰 인터페이스를 통해 다시 호출하여 모든 UI 기술이 포함 된 뷰가 OpenFileDialog. 이것은 발표자가 사용중인 UI 기술과 관련된 작업을 수행 할 수 없다는 점에서 매우 중요한 차이점입니다.

그러면 선택한 파일이 발표자에게 반환되어 논리가 계속됩니다. 여기에는 파일 처리를 처리해야하는 모델이나 서비스가 포함될 수 있습니다.

MVP 패턴 인 imo를 사용하는 주된 이유는 뷰 로직에서 UI 기술을 분리하기 위해서입니다. 따라서 발표자는 뷰가 UI 로직과 분리 된 상태를 유지하는 동안 모든 로직을 조정합니다. 이것은 발표자를 완전히 단위 테스트 가능하게 만드는 아주 좋은 부작용이 있습니다.

업데이트 : 발표자는 하나의 특정보기 에서 발견 된 논리의 구현이므로 보기-발표자 관계는 IMO 일대일 관계입니다. 그리고 모든 실용적인 목적을 위해 하나의보기 인스턴스 (예 : Form)가 하나의 발표자 인스턴스와 상호 작용하고 하나의 발표자 인스턴스는 하나의보기 인스턴스와 만 상호 작용합니다.

즉, WinForms로 MVP를 구현할 때 발표자는 항상 뷰의 UI 기능을 나타내는 인터페이스를 통해 뷰와 상호 작용합니다. 이 인터페이스를 구현하는 뷰에는 제한이 없으므로 서로 다른 "위젯"이 동일한 뷰 인터페이스를 구현하고 프레젠터 클래스를 재사용 할 수 있습니다.


감사. 그래서 presenter.OpenFile () 메서드에서 openfiledialog를 표시하는 코드가 없어야합니까? 대신 해당 창을 표시하려면보기로 돌아 가야합니까?
Darren Young

4
맞습니다. 발표자가 직접 대화 상자를 열도록 두지 않을 것입니다. 테스트를 망칠 수 있기 때문입니다. 뷰에 오프로드하거나 일부 시나리오에서 수행 한 것처럼 별도의 "FileOpenService"클래스가 실제 대화 상자 상호 작용을 처리하도록합니다. 이렇게하면 테스트 중에 파일 열기 서비스를 위조 할 수 있습니다. 이러한 코드를 별도의 서비스에 넣으면 재사용 성 부작용이 발생할 수 있습니다. :)
Peter Lillevold 2011 년

2

발표자는 사용자가 제안한대로 openfiledialog 창을 표시하는 요청 끝에 조치를 취해야합니다. 모델에서 데이터가 필요하지 않기 때문에 발표자는 요청을 처리 할 수 ​​있으며 처리해야합니다.

모델에 일부 항목을 생성하기 위해 데이터가 필요하다고 가정 해 보겠습니다. 스트림에서 엔티티를 생성하는 방법이있는 액세스 레이어로 스트림 트로프를 전달할 수 있지만, 발표자에서 파일 구문 분석을 처리하고 모델의 엔티티 당 생성자 또는 Create 메소드를 사용하는 것이 좋습니다.


1
응답 해 주셔서 감사합니다. 또한보기에 대해 한 명의 발표자가 있습니까? 발표자는 요청을 처리하거나 데이터가 필요한 경우 특정 요청에 따라 작동하는 여러 모델 클래스에 위임합니까? 그게 올바른 방법입니까? 다시 한 번 감사드립니다.
Darren Young

3
보기에는 발표자가 한 명 있지만 발표자는 여러보기를 가질 수 있습니다.
JochemKempe 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.