Uwagi dotyczące implementacji Model-View-Presenter


34

Próbuję dobrze zrozumieć, jak zaimplementować dobre oddzielenie interfejsu użytkownika od modelu, ale mam problem z ustaleniem, gdzie dokładnie podzielić linie.

Patrzyłem na Model-View-Presenter, ale nie jestem pewien, jak dokładnie go wdrożyć. Na przykład mój widok ma wiele okien dialogowych.

  • Czy powinna istnieć klasa View z instancjami każdego z okien dialogowych? W takim razie, w jaki sposób okna dialogowe powinny współdziałać z Prezenterem? to znaczy. jeśli indywidualne okno dialogowe musi żądać danych z Modelu za pośrednictwem Prezentera, w jaki sposób dialog powinien uzyskać odniesienie do Prezentera? Przez odniesienie do widoku podanego mu podczas budowy?
  • Myślałem, że może widok powinien być klasą statyczną? Następnie okna dialogowe GetView i stamtąd prezentera ...
  • Myślałem o skonfigurowaniu Prezentera z właścicielem Widoku i Modelu (w przeciwieństwie do Widoku posiadającego Prezentera i Prezentera posiadającego Model) oraz Prezentera rejestrującego wywołania zwrotne dla zdarzeń w Widoku, ale to sprawia, że ​​wydaje się to dużo bardziej sprzężone (lub przynajmniej zależne od języka).

Staram się:

  1. uczyń to możliwie jak najbardziej oddzielonym
  2. idealnie umożliwiają para Prezenter / model z widokiem innych językach (ja nie zrobiłem mnóstwo inter-językowego rzeczy, ale wiem, że jest to możliwe, szczególnie tym bardziej void(void)mogę trzymać się przynajmniej C # app z Biblioteka C ++ ...
  3. utrzymuj kod w czystości i prostocie

Więc ... jakieś sugestie, jak należy obsługiwać interakcje?


Czy obejrzałeś ten artykuł ?: en.wikipedia.org/wiki/Model-view-presenter
Bernard

1
Mam… Znalazłem to trochę szybko i na wysokim poziomie, chcę lepiej zrozumieć, jak obsługiwać wiele okien dialogowych w dużym projekcie z możliwie najmniejszym sprzężeniem.
Trycatch

Odpowiedzi:


37

Witamy na śliskim stoku. W tym momencie zdałeś sobie sprawę, że istnieje nieskończona różnorodność wszystkich interakcji między widokiem modelu. MVC, MVP (Taligent, Dolphin, Passive View), MVVM, żeby wymienić tylko kilka.

Wzorzec Prezentera widoku modelu, podobnie jak większość wzorów architektonicznych, jest otwarty na wiele różnorodności i eksperymentów. Jedną wspólną cechą wszystkich wariantów jest rola prezentera jako „pośrednika” między widokiem a modelem. Dwa najczęstsze to widok pasywny i nadzorujący prezenter / kontroler - [ Fowler ]. Widok pasywny traktuje interfejs użytkownika jako bardzo płytki interfejs między użytkownikiem a prezenterem. Zawiera bardzo mało, jeśli w ogóle, logiki, która przenosi tyle odpowiedzialności na prezentera. Nadzór nad prezenterem / kontrolerempróbuje wykorzystać powiązanie danych wbudowane w wiele platform interfejsu użytkownika. Interfejs użytkownika obsługuje synchronizację danych, ale prezenter / kontroler wkracza, aby uzyskać bardziej złożoną logikę. W obu przypadkach model, widok i prezenter tworzą triadę

Istnieje wiele sposobów, aby to zrobić. Bardzo często postrzegane jest to przez traktowanie każdego okna dialogowego / formularza jako innego widoku. Wiele razy istnieje stosunek 1: 1 między widokami a prezenterami. To nie jest trudna, szybka zasada. Dość często zdarza się, że jeden prezenter obsługuje wiele powiązanych widoków i odwrotnie. Wszystko zależy od złożoności widoku i złożoności logiki biznesowej.

Jeśli chodzi o sposób, w jaki widoki i prezenterzy uzyskują odniesienia do siebie, jest to czasami nazywane okablowaniem . Masz trzy możliwości:

Widok zawiera odniesienie do prezentera
Formularz lub okno dialogowe implementuje widok. Formularz ma procedury obsługi zdarzeń, które są przekazywane do prezentera za pomocą bezpośrednich wywołań funkcji:

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

Ponieważ prezenter nie ma odniesienia do widoku, widok musi przesłać mu dane jako argumenty. Prezenter może komunikować się z widokiem za pomocą funkcji zdarzeń / wywołań zwrotnych, których widok musi nasłuchiwać.

Prezenter posiada odniesienie do widoku
W scenariuszu widok przedstawia właściwości danych, które wyświetla użytkownikowi. Prezenter nasłuchuje zdarzeń i manipuluje właściwościami w widoku:

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

Oba zawierają odniesienia do siebie, tworząc zależność cykliczną. Z
tym scenariuszem łatwiej jest pracować niż z innymi. Widok reaguje na zdarzenia, wywołując metody w prezencie. Prezenter odczytuje / modyfikuje dane z widoku poprzez odsłonięte właściwości.

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

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

Istnieją inne kwestie, które należy wziąć pod uwagę przy wzorcach MVP. Kolejność tworzenia, czas życia obiektu, w którym odbywa się okablowanie, komunikacja między triadami MVP, ale ta odpowiedź urosła już wystarczająco długo.


1
To jest zdecydowanie pomocne. Komunikacja między triadami a życiem jest obecnie trudna, ponieważ teraz rozumiem.
trycatch 23.03.11

8

Jak wszyscy powiedzieli, jest mnóstwo opinii i żadna z nich nie jest dobra ani zła. Bez wchodzenia w niezliczoną liczbę wzorców i skupianie się wyłącznie na MVP, oto kilka sugestii dotyczących implementacji.

Trzymaj je oddzielnie. Widok powinien implementować interfejs, który tworzy więź między widokiem a prezenterem. Widok tworzy prezentera i wstrzykuje się do prezentera oraz przedstawia metody oferowane prezenterowi do interakcji z widokiem. Widok odpowiada za wdrożenie tych metod lub właściwości w dowolny sposób. Zasadniczo masz jeden widok: jeden prezenter, ale w niektórych przypadkach możesz mieć wiele widoków: jeden prezenter (web, wpf itp.). Kluczem tutaj jest to, że prezenter nie wie nic o implementacjach interfejsu użytkownika i tylko wchodzi w interakcję z widokiem przez interfejs.

Oto przykład. Najpierw mamy klasę widoku z prostą metodą wyświetlania wiadomości użytkownikowi:

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

Teraz jest prezenter. Zauważ, że prezenter pobiera IView do swojego konstruktora.

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

Oto rzeczywisty interfejs użytkownika. Może to być okno, okno dialogowe, strona internetowa itp. Nie ma znaczenia. Uwaga: konstruktor widoku utworzy prezentera, wstrzykując się do niego.

class View : IView
{
  private Presenter _presenter;

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

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

Prezenter nie dba o to, jak widok implementuje metodę, którą właśnie wykonuje. Jak wszyscy prezenter wie, może to być zapisywanie do pliku dziennika, a nawet nie pokazywanie go użytkownikowi.

W każdym razie prezenter trochę pracuje z modelem na zapleczu i w pewnym momencie chce poinformować użytkownika o tym, co się dzieje. Więc teraz mamy gdzieś w prezencie metodę, która woła do widoku wiadomości InformUser.

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

To tutaj dostajesz swoje oddzielenie. Prezenter ma tylko odniesienie do implementacji IView i tak naprawdę nie obchodzi go, jak jest on implementowany.

Jest to również słaba implementacja mans, ponieważ w widoku masz odniesienie do Prezentera, a obiekty są ustawiane za pomocą konstruktorów. W bardziej niezawodnym rozwiązaniu prawdopodobnie chciałbyś spojrzeć na odwrócenie kontenerów kontroli (IoC), takich jak Windsor, Ninject itp., Które rozwiązałyby dla ciebie implementację IView w czasie wykonywania na żądanie, a tym samym uczyniłyby ją jeszcze bardziej oddzieloną.


4

Myślę, że ważne jest, aby pamiętać, że kontroler / prezenter to miejsce, w którym akcja naprawdę ma miejsce. Sprzężenie w sterowniku jest nieuniknione ze względu na konieczność.

Podstawowym punktem kontrolera jest to, że jeśli wprowadzisz zmianę w widoku, wówczas model nie musi się zmieniać i odwrotnie (jeśli model się zmienia, widok nie musi albo), ponieważ kontroler tłumaczy Modeluj w widoku iz powrotem. Ale kontroler zmieni się, gdy zrobią to zmiany modelu lub widoku, ponieważ musisz skutecznie tłumaczyć w kontrolerze, jak model ma być oglądany, jak wprowadzić zmiany wprowadzone w widoku z powrotem do trybu.

Najlepszy przykład, jaki mogę podać, to to, że kiedy piszę aplikację MVC, mogę nie tylko mieć dane w widoku GUI, ale mogę także napisać procedurę, która wypycha dane pobrane z Modelu do postaci stringwyświetlanej w debuggerze (i przez rozszerzenie do zwykłego pliku tekstowego). Jeśli mogę pobrać dane modelu i przetłumaczyć je swobodnie na tekst bez zmiany widoku lub modelu i tylko kontrolera, to jestem na dobrej drodze.

To powiedziawszy, będziesz musiał mieć odniesienia między różnymi komponentami, aby wszystko działało. Kontroler musi wiedzieć o widoku, aby przesyłać dane, widok musi wiedzieć o kontrolerze, aby poinformować go, kiedy wprowadzono zmianę (na przykład, gdy użytkownik kliknie „Zapisz” lub „Nowy ...”). Kontroler musi wiedzieć o modelu, aby pobrać dane, ale argumentowałbym, że model nie powinien wiedzieć o niczym innym.

Zastrzeżenie: Pochodzę z całkowicie Mac, Objective-C, kakao, które naprawdę popycha cię do paradygmatu MVC, czy chcesz, czy nie.


To zdecydowanie mój cel. Moim głównym problemem jest to, jak skonfigurować widok - czy powinna to być klasa z instancją każdego okna dialogowego, a następnie użyć View.Getters, które wywołują Dialog.Getters, czy też Prezenter powinien mieć możliwość bezpośredniego połączenia z Dialog.Getters ( wydaje się to zbyt mocno powiązane, więc prawdopodobnie nie?)
wytrop

Myślę, że Prezenter / Kontroler powinien być w pełni odpowiedzialny za Widoki, więc ten ostatni. Ponownie, pewne sprzężenie musi się zdarzyć, ale przynajmniej jeśli kierunek odpowiedzialności jest jasny, konserwacja powinna być łatwiejsza na dłuższą metę.
Philip Regan

2
Z pewnością zgadzam się, że P / C powinien być odpowiedzialny za widok, ale pomyślałem, że po części to, co miało sprawić, że MVP było potężne, to zdolność do wyciągnięcia całej biblioteki interfejsu użytkownika i podłączenia nowej i do niej masowania (dllimporting i tak dalej) być w stanie uruchomić kolejny na swoim miejscu. Czy nie byłoby to trudniejsze, gdyby kontroler / prezenter miał bezpośredni dostęp do okien dialogowych? Na pewno nie próbuję się kłócić, po prostu dalej rozumiem :)
trycatch

Myślę, że prawdziwa moc pochodzi z dwóch kierunków: po pierwsze, że widok i model nie mają nic wspólnego z innymi, a po drugie, że większość prac programistycznych, czyli silnika aplikacji, jest starannie zamknięta jednostka, kontroler. Ale musi nastąpić krwawienie z odpowiedzialności. Przynajmniej większość zamian interfejsów zostanie wykonana w kontrolerze, a wszelkie połączenia z Widoku będą minimalne. Jak powiedzieli inni, pewne krwawienie logiki jest oczekiwane i dozwolone. MVC nie jest magiczną kulą.
Philip Regan

Ważnym punktem do oddzielenia jest to, że prezenter uzyskuje dostęp TYLKO przez dobrze zdefiniowane interfejsy (niezależne od biblioteki interfejsu użytkownika), dzięki czemu można zastąpić bibliotekę interfejsu użytkownika innym (innym, który implementuje ten sam interfejs dla formularza / okna / okna dialogowego / strony / control / cokolwiek)
Marcel Toth

2

Ogólnie rzecz biorąc, chcesz, aby Twój model zawierał wszystkie interakcje z tym modelem. Na przykład wszystkie działania CRUD (Utwórz, Odczytaj, Aktualizuj, Usuń) są częścią modelu. To samo dotyczy specjalnych obliczeń. Jest kilka dobrych powodów:

  • Łatwiej jest zautomatyzować testowanie tego kodu
  • Przechowuje wszystkie ważne rzeczy w jednym miejscu

W kontrolerze (aplikacji MVC) wszystko, co robisz, to zbieranie modeli, których potrzebujesz w swoim widoku, i wywoływanie odpowiednich funkcji w modelu. Wszelkie zmiany stanu modelu mają miejsce w tej warstwie.

Twój Widok wyświetla po prostu przygotowane modele. Zasadniczo widok odczytuje tylko model i odpowiednio dostosowuje jego wynik.

Mapowanie ogólnej zasady do rzeczywistych klas

Pamiętaj, że twoje okna dialogowe są widokami. Jeśli masz już klasę okna dialogowego, nie ma powodu, aby tworzyć inną klasę „Widok”. Warstwa Presenter zasadniczo wiąże model z kontrolkami w Widoku. Logika biznesowa i wszystkie ważne dane są przechowywane w modelu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.