Model-View-Presenter 구현 생각


34

UI와 모델간에 좋은 디커플링을 구현하는 방법에 대해 잘 이해하려고 노력하고 있지만 정확하게 줄을 나눌 위치를 알아내는 데 어려움을 겪고 있습니다.

Model-View-Presenter를 살펴 봤지만 구현 방법을 정확히 모릅니다. 예를 들어, 내보기에는 여러 개의 대화 상자가 있습니다.

  • 각 대화 상자의 인스턴스가있는 View 클래스가 있어야합니까? 그렇다면이 대화 상자가 발표자와 어떻게 상호 작용해야합니까? 즉. 개별 대화 상자에서 발표자를 통해 모델의 데이터를 요청해야하는 경우 대화 상자에서 발표자에 대한 참조를 가져와야합니까? 시공 중 제공된 뷰에 대한 참조를 통해?
  • 뷰가 정적 클래스 여야한다고 생각하고 있었습니까? 그런 다음 GetView 대화 상자에서 발표자를 가져옵니다.
  • 뷰와 모델의 소유권 (발표자와 발표자가 모델이있는 뷰와 반대)과 발표자가 뷰의 이벤트에 대한 콜백을 등록하는 발표자를 설정하려고 생각했지만 많이 보입니다. 더 많은 결합 (또는 적어도 언어에 의존)

나는 노력하고있다 :

  1. 이것을 가능한 한 분리하십시오
  2. 발표자 / 모델을 다른 언어의 뷰와 결합하는 것이 이상적입니다 (많은 언어 간 작업을 수행하지는 않았지만 특히 void(void)C # 앱이있는 C # 앱을 더 많이 사용할 수 있음을 알고 있습니다. C ++ 라이브러리 ...
  3. 코드를 깨끗하고 단순하게 유지

상호 작용이 어떻게 처리되어야하는지에 대한 제안이 있으십니까?


이 기사를 보셨습니까? : en.wikipedia.org/wiki/Model-view-presenter
Bernard

1
나는 조금 빠르며 높은 레벨을 발견했다. 가능한 한 적은 결합으로 큰 프로젝트에서 여러 대화를 처리하는 방법을 더 잘 이해하려고한다.
trycatch

답변:


37

미끄러운 경사면에 오신 것을 환영합니다. 이 시점에서 모든 모델 뷰 상호 작용의 끝없는 변형이 있음을 깨달았습니다. MVC, MVP (Taligent, Dolphin, Passive View), MVVM 등이 그 예입니다.

대부분의 건축 패턴과 마찬가지로 Model View Presenter 패턴은 다양한 다양성과 실험에 열려 있습니다. 모든 변형이 공통적으로 갖는 한 가지는 뷰와 모델 사이의 "중간자"로서 발표자의 역할입니다. 가장 일반적인 두 가지는 수동보기감독 발표자 / 컨트롤러 -[ 파울러 ]입니다. 수동보기 는 UI를 사용자와 발표자 간의 매우 얕은 인터페이스로 취급합니다. 발표자에게 많은 책임을 위임하는 논리가 거의 포함되어 있지 않습니다. 감독 발표자 / 컨트롤러많은 UI 프레임 워크에 내장 된 데이터 바인딩을 활용하려고합니다. UI는 데이터 동기화를 처리하지만보다 복잡한 논리를 위해 발표자 / 컨트롤러 단계를 처리합니다. 두 경우 모두 모델,보기 및 발표자가 트라이어드를 형성합니다.

이를 수행하는 방법에는 여러 가지가 있습니다. 각 대화 상자 / 양식을 다른보기로 처리하여이를 처리하는 것이 매우 일반적입니다. 많은 경우 뷰와 발표자간에 1 : 1 관계가 있습니다. 이것은 어렵고 빠른 규칙이 아닙니다. 한 발표자가 여러 관련보기를 처리하거나 그 반대의 경우도 마찬가지입니다. 그것은 모두보기의 복잡성과 비즈니스 로직의 복잡성에 달려 있습니다.

뷰와 발표자가 서로에 대한 참조를 얻는 방법에 관해서는 이것을 때로는 배선 이라고 합니다. 세 가지 선택이 있습니다.

보기는 발표자에 대한 참조를 보유합니다
. 양식 또는 대화 상자는보기를 구현합니다. 이 양식에는 직접 함수 호출을 사용하여 발표자에게 전달되는 이벤트 핸들러가 있습니다.

MyForm.SomeEvent(Sender)
{
  Presenter.DoSomething(Sender.Data);
}

발표자는 뷰에 대한 참조를 가지지 않으므로 뷰는 뷰로 데이터를 인수로 보내야합니다. 발표자는보기에서 수신해야하는 이벤트 / 콜백 기능을 사용하여보기와 다시 통신 할 수 있습니다.

발표자는보기에 대한 참조를 보유합니다
. 시나리오에서보기는 사용자에게 표시되는 데이터의 속성을 표시합니다. 발표자는 이벤트를 수신하고보기의 특성을 조작합니다.

Presenter.SomeEvent(Sender)
{
  DomainObject.DoSomething(View.SomeProperty);
  View.SomeOtherProperty = DomainObject.SomeData;
}

둘 다 순환 종속성을 형성하는 서로에 대한 참조를 보유합니다.
이 시나리오는 실제로 다른 시나리오보다 작업하기가 더 쉽습니다. 보기는 발표자에서 메소드를 호출하여 이벤트에 응답합니다. 발표자는 노출 된 속성을 통해보기에서 데이터를 읽거나 수정합니다.

View.SomeEvent(Sender)
{
  Presenter.DoSomething();
}

Presenter.DoSomething()
{
  View.SomeProperty = DomainObject.Calc(View.SomeProperty);
}

MVP 패턴과 관련하여 고려해야 할 다른 문제가 있습니다. 결선 순서, 객체 수명, 배선이 이루어지는 위치, MVP 트라이어드 간의 통신이지만이 답변은 이미 오래 전부터 성장했습니다.


1
이것은 확실히 도움이됩니다. 트라이어드와 수명 사이의 커뮤니케이션은 현재이 중 일부를 파악하고 있기 때문에 현재 어려움을 겪고 있습니다.
trycatch

8

모든 사람들이 말했듯이, 수십 개의 의견이 있으며 그중 하나가 옳고 그른 것은 아닙니다. 수많은 패턴에 들어 가지 않고 MVP에만 초점을 맞추지 않고 구현에 대한 몇 가지 제안이 있습니다.

분리하십시오. 뷰는 뷰와 발표자 사이의 결합을 형성하는 인터페이스를 구현해야합니다. 뷰는 발표자를 생성하고 발표자에게 자신을 주입하고 발표자가 뷰와 상호 작용할 수있는 방법을 제공합니다. 뷰는 이러한 방법이나 속성을 원하는 방식으로 구현할 책임이 있습니다. 일반적으로 하나의 발표자 : 발표자 한 명이지만 경우에 따라 하나의 발표자 (웹, wpf 등)가 있습니다. 여기서 핵심은 발표자가 UI 구현을 전혀 모르고 인터페이스를 통해서만 뷰와 상호 작용한다는 것입니다.

다음은 예입니다. 먼저 간단한 방법으로 사용자에게 메시지를 표시하는 뷰 클래스가 있습니다.

interface IView
{
  public void InformUser(string message);
}

이제 발표자가 있습니다. 발표자는 IView를 생성자로 가져옵니다.

class Presenter
{
  private IView _view;
  public Presenter(IView view)
  {
    _view = view;
  }
}

이제 실제 사용자 인터페이스가 있습니다. 이것은 창, 대화 상자, 웹 페이지 등이 될 수 있습니다. 중요하지 않습니다. 뷰의 생성자는 발표자 자신을 주입하여 발표자를 만듭니다.

class View : IView
{
  private Presenter _presenter;

  public View()
  {
    _presenter = new Presenter(this);
  }

  public void InformUser(string message)
  {
    MessageBox.Show(message);
  }
}

발표자는 뷰가 방금 수행하는 메소드를 구현하는 방법에 신경 쓰지 않습니다. 발표자가 아는 한, 로그 파일에 쓰거나 사용자에게 표시하지 않을 수도 있습니다.

어쨌든 발표자는 백엔드에서 모델에 대한 작업을 수행하고 어떤 시점에서 사용자에게 진행 상황을 알리려고합니다. 이제 발표자 어딘가에 뷰 InformUser 메시지를 호출하는 메소드가 있습니다.

class Presenter
{
  public void DoSomething()
  {
    _view.InformUser("Starting model processing...");
  }
}

이곳은 디커플링을 얻는 곳입니다. 발표자는 IView 구현에 대한 참조 만 보유하며 실제로 구현 방법에 대해서는 신경 쓰지 않습니다.

뷰에서 Presenter에 대한 참조가 있고 객체가 생성자를 통해 설정되므로 이는 잘못된 mans 구현입니다. 보다 강력한 솔루션에서는 Windsor, Ninject 등과 같은 IoC (Inversion of Control) 컨테이너를보고 필요할 때 런타임에 IView 구현을 해결하여 더욱 분리 할 수 ​​있습니다.


4

Controller / Presenter가 작업이 실제로 이루어지는 곳임을 기억하는 것이 중요하다고 생각합니다. 컨트롤러의 커플 링은 필연적으로 필요합니다.

Controller의 핵심 포인트는 View를 변경 하면 Controller가 다음과 같이 변환되기 때문에 Model 을 변경할 필요가 없으며 Model을 변경할 필요가 없으며 그 반대도 마찬가지입니다 (Model이 View를 변경할 필요가없는 경우). 뷰로 모델링 한 후 다시 돌아옵니다. 그러나 모델 또는보기가 변경되면 컨트롤러에서 모델을 보는 방법을 효과적으로 변환해야하기 때문에 컨트롤러가 변경됩니다.보기에서 변경 사항을 다시 모드로 다시 가져 오는 방법.

내가 줄 수있는 가장 좋은 예는 MVC 앱을 작성할 때 GUI보기에 데이터를 가질 수있을뿐만 아니라 모델에서 가져온 데이터를 string디버거에 표시 하도록 푸시하는 루틴을 작성할 수도 있다는 것입니다 (그리고 일반 텍스트 파일로 확장). 모델 데이터를 가져 와서 뷰 또는 모델을 변경하지 않고 텍스트 만으로 자유롭게 번역 할 수 있다면 올바른 경로에 있습니다.

즉, 모든 구성 요소를 작동 시키려면 다른 구성 요소간에 참조가 있어야합니다. Controller는 데이터를 푸시하기 위해 View에 대해 알아야하고, View는 사용자가 "Save"또는 "New ..."를 클릭 할 때와 같이 변경이있을 때이를 알려주기 위해 Controller에 대해 알아야합니다. 컨트롤러는 데이터를 가져 오기 위해 모델에 대해 알아야하지만 모델은 다른 것에 대해 알지 않아야한다고 주장합니다.

주의 사항 : 나는 완전히 Mac, Objective-C, Cocoa 배경에서 왔으며, 원하는지 여부에 관계없이 MVC 패러다임으로 당신을 정말로 밀어 넣습니다.


이것은 분명히 나의 목표입니다. 내 주요 문제는 View를 설정하는 방법입니다. 각 대화 상자의 인스턴스가있는 클래스인지 Dialog.Getters를 호출하는 View.Getters를 사용하거나 Presenter가 Dialog.Getters를 직접 호출 할 수 있는지 여부입니다. 이것은 너무 밀접하게 결합되어있는 것 같습니다. 아마 아닐까요?)
trycatch

발표자 / 컨트롤러가 뷰에 대한 책임을 져야한다고 생각합니다. 다시 말하지만, 어떤 결합은 일어날 수밖에 없지만 최소한 책임의 방향이 분명하다면 장기적으로 유지 보수가 더 쉬워야합니다.
Philip Regan

2
필자는 P / C가 View를 책임 져야한다는 데 동의하지만 MVP를 강력하게 만드는 것은 UI 라이브러리 전체를 꺼내어 새로운 라이브러리를 플러그인하는 기능 (dllimporting 및 notnot)이라고 생각했다. 그 자리에서 다른 것을 실행할 수 있습니다. Controller / Presenter가 대화 상자에 직접 액세스하는 것이 더 어렵지 않습니까? 나는 확실히 논쟁하려고하지 않고 단지 더 이해한다 :)
trycatch

진정한 힘은 두 가지 방향에서 비롯된 것이라고 생각합니다. 첫 번째는 뷰와 모델이 다른 것과 관련이 없으며 두 번째는 대부분의 개발 작업 인 앱 엔진이 깔끔하게 포함되어 있다는 것입니다. 단위, 컨트롤러. 그러나 약간의 책임 출혈이 일어날 수밖에 없다. 인터페이스의 대부분의 스와핑은 컨트롤러에서 수행되며 View와의 연결은 최소가됩니다. 다른 사람들이 말했듯이, 일부 논리적 인 출혈이 예상되고 허용됩니다. MVC는 마법의 총알이 아닙니다.
Philip Regan

디커플링의 중요한 점은 발표자가 잘 정의 된 인터페이스 (UI 라이브러리와 무관)를 통해서만보기에 액세스하므로 UI ​​라이브러리를 다른 라이브러리 (양식 / 창 / 대화 상자 / 페이지에 대해 동일한 인터페이스를 구현하는 다른 라이브러리)로 대체 할 수 있다는 것입니다. / control / whatever)
Marcel Toth

2

일반적으로 모델이 해당 모델과의 모든 상호 작용을 캡슐화하기를 원합니다. 예를 들어 CRUD 작업 (만들기, 읽기, 업데이트, 삭제)은 모두 모델의 일부입니다. 특별한 계산에서도 마찬가지입니다. 이에 대한 몇 가지 이유가 있습니다.

  • 이 코드에 대한 테스트를 자동화하는 것이 더 쉽습니다.
  • 중요한 모든 것을 한 곳에 보관합니다

컨트롤러 (MVC 앱)에서 뷰에서 사용해야하는 모델을 수집하고 모델에서 적절한 기능을 호출하기 만하면됩니다. 이 상태에서는 모델 상태가 변경됩니다.

뷰에는 단순히 준비한 모델이 표시됩니다. 기본적으로보기는 모델을 읽고 그에 따라 출력을 조정합니다.

일반적인 원리를 실제 클래스에 매핑

대화는보기입니다. 대화 상자 클래스가 이미 있으면 다른 "보기"클래스를 만들 필요가 없습니다. Presenter 레이어는 기본적으로 모델을 View의 컨트롤에 바인딩합니다. 비즈니스 로직과 모든 중요한 데이터가 모델에 저장됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.