Według opisu MVP Martina Fowlera ( http://martinfowler.com/eaaDev/uiArchs.html )
O części MVC „Widok” Fowler mówi:
Pierwszym elementem Potela jest traktowanie widoku jako struktury widżetów, widżetów odpowiadających kontrolkom modelu Forms and Controls i usunięcie wszelkich separacji widok / kontroler. Widok MVP jest strukturą tych widżetów. Nie zawiera żadnego zachowania opisującego reakcję widżetów na interakcję użytkownika .
(Moje pogrubione wyróżnienie)
Następnie prezenter:
Aktywna reakcja na działania użytkownika żyje w oddzielnym obiekcie prezentera. Podstawowe widżety dla gestów użytkownika nadal istnieją w widżetach, ale te uchwyty jedynie przekazują kontrolę prezenterowi .
Prezenter następnie decyduje, jak zareagować na zdarzenie. Potel omawia tę interakcję przede wszystkim w kontekście działań na modelu, które wykonuje za pomocą systemu poleceń i selekcji. Przydatną rzeczą do podkreślenia tutaj jest podejście polegające na pakowaniu wszystkich zmian w modelu za pomocą polecenia - zapewnia to dobrą podstawę do zachowania cofania / ponawiania.
(Ponownie, moje pogrubione wyróżnienie)
Tak więc, zgodnie z wytycznymi Fowlera, Twój Widok nie powinien być odpowiedzialny za jakiekolwiek zachowanie w odpowiedzi na zdarzenie przycisku; który obejmuje utworzenie wystąpienia UserInfo
. Odpowiedzialność za podjęcie decyzji o utworzeniu obiektu należy do metody Presenter, do której przekazywane jest zdarzenie interfejsu użytkownika.
Jednak można również argumentować, że moduł obsługi zdarzenia przycisku Widoku nie powinien być również odpowiedzialny za przekazywanie zawartości textView
któregoś z nich, ponieważ Widok powinien po prostu przekazywać zdarzenie przycisku do Prezentera i nic więcej.
W przypadku MVP widok często implementuje interfejs, za pomocą którego prezenter może pobierać dane bezpośrednio z widoku (jednocześnie zapewniając, że prezenter nadal jest niezależny od samego widoku). Ponieważ UserInfo jest prostym POJO, może być ważne, aby widok ujawnił getter dla UserInfo, który Prezenter może pobrać z Widoku poprzez interfejs.
// The view would implement IView
public interface IView {
public UserInfo GetUserInfo();
}
// Presenter
public class AddUserPresenter {
private IView addUserView;
public void SetView(IView view) {
addUserView = view
}
public void onSomethingClicked() {
UserInfo userInfo = addUserView.GetUserInfo();
// etc.
}
}
Czym to się różni od przekazania UserInfo
bezpośrednio do widoku za pomocą procedury obsługi zdarzeń? Główną różnicą jest to, że prezenter nadal jest ostatecznie odpowiedzialny za logikę, która powoduje utworzenie UserInfo
obiektu. tzn. wydarzenie dotarło do Prezentera przed jego utworzeniem UserInfo
, umożliwiając Prezenterowi podjęcie decyzji.
Wyobraź sobie scenariusz, w którym masz logikę prezentera, w której nie chcesz, UserInfo
aby była tworzona na podstawie pewnego stanu w widoku. Na przykład, jeśli użytkownik nie zaznaczył pola wyboru w widoku lub miałeś sprawdzenie poprawności względem pola, które ma zostać dodane do UserInfo, które nie powiodło się - Twój prezenter może zawierać dodatkowe sprawdzenie przed wywołaniem GetUserInfo
- tj.
private boolean IsUsernameValid() {
String username = addUserView.GetUsername();
return (username != null && !username.isEmpty());
}
public void onSomethingClicked() {
if (IsUsernameValid()) {
UserInfo userInfo = addUserView.GetUserInfo();
// etc.
}
}
Ta logika pozostaje w prezenterie i nie musi być dodawana do widoku. Gdyby widok był odpowiedzialny za wywołanie GetUserInfo()
, byłby również odpowiedzialny za logikę związaną z jego użyciem; tego właśnie próbuje uniknąć wzór MVP.
Tak więc, chociaż metoda, która tworzy, która UserInfo
fizycznie może istnieć w klasie View, nigdy nie jest wywoływana z klasy View, tylko z Presenter.
Oczywiście, jeśli tworzenie UserInfo
końcowych elementów wymaga dodatkowej kontroli zawartości widżetów wprowadzanych przez użytkownika (np. Konwersja ciągów, sprawdzanie poprawności itp.), Lepiej byłoby udostępnić poszczególne moduły pobierające dla tych rzeczy, aby można było sprawdzić poprawność / konwersję ciągów miejsce w Prezenterie - a następnie prezenter tworzy Twój UserInfo
.
Ogólnie rzecz biorąc, twoim głównym celem w odniesieniu do oddzielenia Prezentera / Widoku jest zapewnienie, że nigdy nie będziesz musiał pisać logiki w widoku. Jeśli kiedykolwiek będziesz musiał dodać if
instrukcję z dowolnego powodu (nawet jeśli jest to if
instrukcja dotycząca stanu właściwości widgetu - zaznaczenie pustego pola tekstowego lub wartość logiczna dla pola wyboru), to należy do prezentera.
onSomethingClicked()
, więc kiedy użytkownik kliknie „coś”, wywołuje widokpresenter.onSomethingClicked()
? Czy w moim przypadku moje metody prezentera powinny być nazwane zamierzonymi działaniamiaddUser()
?Presenter
Jest oczywiście odpowiedzialny za UI logiki zamiast logiki domeny, a specjalnie dostosowane doView
, więc pojęcia, które powinny istnieć to pojęcia UI, więc metoda nazwanaonSomethingClicked()
jest rzeczywiście właściwe. Z perspektywy czasu nazwa, którą wybrałem w powyższym przykładzie, nie pachnie całkiem dobrze :-).GetUserInfo
metody w widoku, o którym wspomniałeś (zostanie uruchomiony z prezentera) Co z możliwymiif
warunkami wGetUserInfo
metodzie? Może niektóre pola UserInfo zostaną ustawione na podstawie reakcji użytkownika? Scenariusz: być może użytkownik zaznaczy pole wyboru, a następnie niektóre nowe komponenty (być może nowy EditText) będą widoczne dla użytkownika. W takim przypadkuGetUserInfo
metoda będzie miała warunek if. Czy w tym scenariuszuGetUserInfo
nadal obowiązuje?UserInfo
jak model widoku (inaczej „View Model”) - W tym scenariuszu dodałbym do tegoboolean
stan pola wyboru oraz pusty / zerowalnyString
stan pola tekstowegoUserInfo
. Możesz nawet rozważyć zmianę jego nazwy,UserInfoViewModel
jeśli to pomaga myśleć w kategoriach POJO, która jest prawdziwą klasą, której jedynym prawdziwym celem jest umożliwienieUserInfoPresenter
znalezienia informacji o stanie Widoku.