[c#] WinForms의 Model-View-Presenter

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

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

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

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

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

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

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

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

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

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

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

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



답변

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를 통해) 노드의 세부 사항을 가져 와서 이벤트 핸들러 델리게이트가 반환 할 때 뷰가 표시된 노드 세부 사항을 업데이트 할 수 있습니다. 좋은 사용자 경험을 위해 데이터를 가져 오는 속도가 너무 느릴 수있는 경우 이러한 비동기 패턴이 필요할 수 있습니다.


답변

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

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

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

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

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

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


답변

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

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


답변