Co wchodzi w „Kontroler” w „MVC”?

186

Wydaje mi się, że rozumiem podstawowe koncepcje MVC - model zawiera dane i zachowanie aplikacji, widok jest odpowiedzialny za wyświetlanie go użytkownikowi, a kontroler zajmuje się wprowadzaniem danych przez użytkownika. Nie jestem pewien, co dokładnie dzieje się w kontrolerze.

Powiedzmy na przykład, że mam dość prostą aplikację (szczególnie myślę o Javie, ale przypuszczam, że te same zasady obowiązują gdzie indziej). Organizuję mój kod w 3 pakiety o nazwie app.model, app.viewi app.controller.

W app.modelpakiecie mam kilka klas, które odzwierciedlają faktyczne zachowanie aplikacji. Te extends Observablei używaj setChanged()oraz, notifyObservers()aby uruchamiać widoki w celu aktualizacji w razie potrzeby.

app.viewPakiet ma klasę (lub kilka klas dla różnych typów wyświetlacza), który wykorzystuje javax.swingkomponenty do obsługi wyświetlacza. Niektóre z tych komponentów muszą zostać przekazane z powrotem do modelu. Jeśli dobrze rozumiem, Widok nie powinien mieć nic wspólnego z opiniami - powinien to zrobić kontroler.

Więc co właściwie umieszczam w kontrolerze? Czy umieszczam public void actionPerformed(ActionEvent e)w widoku tylko wywołanie metody w kontrolerze? Jeśli tak, to czy należy przeprowadzić weryfikację itp. W kontrolerze? Jeśli tak, to w jaki sposób mam przesyłać komunikaty o błędach z powrotem do Widoku - czy powinno to przejść ponownie przez Model, czy też sterownik powinien po prostu wysłać go z powrotem do Widoku?

Jeśli sprawdzanie poprawności odbywa się w widoku, co mam umieścić w kontrolerze?

Przepraszam za długie pytanie, chciałem tylko udokumentować moje zrozumienie procesu i mam nadzieję, że ktoś może wyjaśnić mi ten problem!

Paul Walker
źródło

Odpowiedzi:

520

W zasugerowanym przez ciebie przykładzie masz rację: „użytkownik kliknął przycisk„ usuń ten element ”w interfejsie, powinien po prostu wywołać funkcję„ usuń ”kontrolera. Kontroler nie ma jednak pojęcia, jak wygląda widok, dlatego musi on gromadzić pewne informacje, takie jak „który element został kliknięty?”

W formie rozmowy:

Widok : „Hej, kontrolerze, użytkownik właśnie powiedział mi, że chce usunąć pozycję 4”.
Kontroler : „Hmm, po sprawdzeniu jego danych uwierzytelniających, może to zrobić ... Hej, model, chcę, żebyś dostał przedmiot 4 i zrobił wszystko, aby go usunąć.”
Model : „Pozycja 4 ... mam. Została usunięta. Wracam do ciebie, kontrolerze”
Kontroler : „Tutaj zgromadzę nowy zestaw danych. Wróć do ciebie, zobacz”.
Widok : „Fajnie, pokażę teraz nowy zestaw użytkownikowi”.

Na końcu tej sekcji masz opcję: albo widok może złożyć osobne żądanie, „daj mi najnowszy zestaw danych”, a tym samym być bardziej czysty, lub administrator domyślnie zwraca nowy zestaw danych za pomocą „usuń " operacja.

Andres Jaan Tack
źródło
90
Ten dialog jest najlepszym wytłumaczeniem MVC, jakie spotkałem, dzięki!
Paul Walker
13
Wszystko w porządku, ale nie ma nic złego w bezpośrednim czytaniu widoku z modelu. „Administratorzy danych nie są policją danych”. Istnieje również doktryna, która mówi, że kontrolery powinny być cienkie. Pomocniki widoku to idealne miejsce do gromadzenia danych gotowych do użycia przez Twój widok. Nie powinno być konieczne wysyłanie całego stosu kontrolera, aby ponownie wykorzystać logikę dostępu do danych. Więcej informacji: rmauger.co.uk/2009/03/…
Wyjątek e
1
Zgadzam się z „Wyjątkiem e”. Dane w modelu mogą być aktualizowane przez wiele zdarzeń, niekoniecznie przez sterownik, a zatem w niektórych projektach MVC M sygnalizuje V, że dane są brudne i V może się odświeżyć. W tym przypadku C nie ma do odegrania żadnej roli.
Mishax
68

Problem MVCpolega na tym, że ludzie myślą, że widok, kontroler i model muszą być od siebie jak najbardziej niezależne. Nie są - widok i kontroler są często ze sobą powiązane - uważają to za M(VC).

Kontroler jest mechanizmem wejściowym interfejsu użytkownika, który jest często zaplątany w widoku, szczególnie w GUI. Niemniej jednak widok jest generowany, a kontroler jest wprowadzany. Widok często może działać bez odpowiedniego kontrolera, ale kontroler jest zwykle znacznie mniej przydatny bez widoku. Przyjazne dla użytkownika kontrolery wykorzystują ten widok do interpretacji danych wejściowych użytkownika w bardziej sensowny i intuicyjny sposób. Właśnie to utrudnia oddzielenie koncepcji kontrolera od widoku.

Pomyśl o sterowanym radiowo robocie na polu wykrywania w zapieczętowanym pudełku jako modelu.

Model opiera się na stanach i przejściach stanu bez koncepcji wyjścia (wyświetlania) lub tego, co wyzwala przejścia stanu. Mogę ustalić pozycję robota na polu, a robot wie, jak zmienić pozycję (zrób krok do przodu / do tyłu / w lewo / w prawo. Łatwy do wyobrażenia bez widoku lub kontrolera, ale nie robi nic użytecznego

Pomyśl o widoku bez kontrolera, np. Ktoś w innym pokoju w sieci w innym pokoju, obserwujący pozycję robota, gdy (x, y) koordynuje spływanie po przewijanej konsoli. Ten widok pokazuje tylko stan modelu, ale ten facet nie ma kontrolera. Ponownie łatwo wyobrazić sobie ten widok bez kontrolera.

Pomyśl o kontrolerze bez widoku, np. Ktoś zamknięty w szafie z kontrolerem radiowym dostrojonym do częstotliwości robota. Ten kontroler wysyła dane wejściowe i powoduje zmiany stanu bez pojęcia o tym, co robią z modelem (jeśli w ogóle). Łatwy do wyobrażenia, ale niezbyt przydatny bez jakiejś informacji zwrotnej z widoku.

Najbardziej przyjazny interfejs użytkownika koordynuje widok ze sterownikiem, zapewniając bardziej intuicyjny interfejs użytkownika. Wyobraź sobie na przykład widok / kontroler z ekranem dotykowym pokazującym aktualną pozycję robota w 2D i pozwalającą użytkownikowi dotknąć punktu na ekranie, który akurat znajduje się przed robotem. Kontroler potrzebuje szczegółowych informacji na temat widoku, np. Pozycji i skali rzutni oraz pozycji piksela dotkniętego punktu względem pozycji piksela robota na ekranie), aby poprawnie zinterpretować to (w przeciwieństwie do faceta zamkniętego w szafie z kontroler radiowy).

Czy odpowiedziałem już na twoje pytanie? :-)

Kontroler to wszystko, co pobiera dane wejściowe od użytkownika, które są używane do spowodowania przejścia modelu w stan przejściowy. Staraj się trzymać widok i kontroler oddzielnie, ale zdaj sobie sprawę, że często są od siebie zależne, więc dobrze jest, jeśli granica między nimi jest rozmyta, tj. Posiadanie widoku i kontrolera jako oddzielnych pakietów może nie być tak dokładnie oddzielone jak ty ale to jest w porządku. Być może będziesz musiał zaakceptować, że kontroler nie zostanie całkowicie oddzielony od widoku, ponieważ widok pochodzi z modelu.

... czy należy przeprowadzić weryfikację itp. w kontrolerze? Jeśli tak, to w jaki sposób mam przesyłać komunikaty o błędach z powrotem do Widoku - czy powinno to przejść ponownie przez Model, czy też sterownik powinien po prostu wysłać go z powrotem do Widoku?

Jeśli sprawdzanie poprawności odbywa się w widoku, co mam umieścić w kontrolerze?

Mówię, że połączony widok i kontroler powinny swobodnie współdziałać bez przechodzenia przez model. Kontroler przyjmuje dane użytkownika i powinien dokonać walidacji (być może przy użyciu informacji z modelu i / lub widoku), ale jeśli weryfikacja się nie powiedzie, kontroler powinien być w stanie bezpośrednio zaktualizować swój powiązany widok (np. Komunikat o błędzie).

Test kwasowy polega na zadaniu sobie pytania, czy niezależny widok (tj. Facet w drugim pokoju obserwujący pozycję robota przez sieć) powinien coś zobaczyć, czy nie, w wyniku błędu sprawdzania poprawności innej osoby (np. Facet w szafie próbował powiedzieć robotowi, żeby zszedł z pola). Zasadniczo odpowiedź brzmi „nie” - błąd weryfikacji uniemożliwił zmianę stanu. Jeśli nie było tranzytu stanu (robot się nie poruszył), nie ma potrzeby mówić innym poglądom. Facet w szafie po prostu nie otrzymał opinii, że próbował spowodować nielegalne przejście (brak widoku - zły interfejs użytkownika) i nikt inny nie musi o tym wiedzieć.

Jeśli facet z ekranem dotykowym próbował wysłać robota poza pole, otrzymał miłą, przyjazną dla użytkownika wiadomość z prośbą, aby nie zabił robota, wysyłając go poza pole wykrywania, ale nikt inny nie musi tego wiedzieć.

Jeśli inne poglądy nie muszą wiedzieć o tych błędów, to są skutecznie mówiąc, że nakłady ze strony użytkownika oraz wszelkie powstałe błędy są częścią modelu i cała sprawa jest trochę bardziej skomplikowane ...

Bert F.
źródło
23

Tutaj jest dobry artykuł na temat podstaw MVC.

W Stanach ...

Kontroler - kontroler tłumaczy interakcje z widokiem na działania, które ma wykonać model.

Innymi słowy, twoja logika biznesowa. Kontroler reaguje na działania podjęte przez użytkownika w widoku i odpowiada. Tutaj umieszczasz sprawdzanie poprawności i wybierasz odpowiedni widok, jeśli sprawdzanie poprawności się nie powiedzie lub powiedzie (strona błędu, okno komunikatu, cokolwiek).

Jest jeszcze jeden dobry artykuł w Fowler .

JP Alioto
źródło
MVP to kolejna opcja omówiona w artykule, do którego się odwołujesz, patrz martinfowler.com/eaaDev/ModelViewPresenter.html
Jon
Dzięki za linki, z pewnością stanowią ciekawą lekturę.
Paul Walker
18

Wzorzec MVC chce jedynie oddzielić prezentację (= widok) od logiki biznesowej (= model). Część kontrolera jest tylko po to, aby spowodować zamieszanie.

Dimitri C.
źródło
1
Dokładnie to, o czym zawsze myślałem do tej pory, ale nigdy nie miałem odwagi nikomu powiedzieć ... lub może nie być w stanie wymyślić odpowiednich słów.
user1451111,
1
Model-widok-zamieszanie
Pada
10

Praktycznie rzecz biorąc, nigdy nie uważałem koncepcji kontrolera za szczególnie przydatną. Używam ścisłej separacji modelu / widoku w kodzie, ale nie ma jasno zdefiniowanego kontrolera. Wydaje się to niepotrzebną abstrakcją.

Osobiście pełnoprawny MVC wydaje się wzorem fabrycznym, ponieważ łatwo prowadzi do mylącego i nadmiernie skomplikowanego projektu. Nie bądź astronautą architektury .

John Kugelman
źródło
9

W oparciu o twoje pytanie mam wrażenie, że jesteś nieco niejasny co do roli Modelki. Model jest utrwalony na danych związanych z aplikacją; jeśli aplikacja ma bazę danych, zadaniem Modelki będzie z nią porozmawiać. Będzie także obsługiwał każdą prostą logikę związaną z tymi danymi; jeśli masz regułę, która mówi, że we wszystkich przypadkach, w których TABLE.foo == "Brawo!" i TABLE.bar == „Huzzah!” następnie ustaw TABLE.field = "W00t!", a następnie chcesz, aby Model się tym zajął.

Kontroler powinien zajmować się większością zachowań aplikacji. Aby odpowiedzieć na twoje pytania:

Czy mogę umieścić publiczną nieważną akcję ActionPerformed (ActionEvent e) w widoku za pomocą wywołania metody w kontrolerze?

Powiedziałbym nie. Powiedziałbym, że powinien żyć w kontrolerze; Widok powinien po prostu przesyłać dane pochodzące z interfejsu użytkownika do kontrolera i pozwolić administratorowi zdecydować, które metody należy wywołać w odpowiedzi.

Jeśli tak, to czy należy przeprowadzić weryfikację itp. W kontrolerze?

Większość twojej weryfikacji powinna naprawdę zostać wykonana przez kontrolera; powinien odpowiedzieć na pytanie, czy dane są poprawne, a jeśli nie, podaj odpowiednie komunikaty o błędach do Widoku. W praktyce możesz wprowadzić kilka prostych sprawdzeń czystości w warstwie Widok w celu poprawy komfortu użytkowania. (Myślę przede wszystkim o środowiskach internetowych, w których możesz chcieć wyświetlać komunikat o błędzie w momencie, gdy użytkownik kliknie „Prześlij”, zamiast czekać na całe przesłanie -> proces -> ładowanie cyklu strony, zanim powie im, że zepsuły się .) Tylko bądź ostrożny; nie chcesz powielać wysiłku bardziej, niż musisz, aw wielu środowiskach (znowu myślę o Internecie) często musisz traktować wszelkie dane pochodzące z interfejsu użytkownika jako pakiet brudnych, brudnych kłamie dopóki ty

Jeśli tak, to w jaki sposób mam przesyłać komunikaty o błędach z powrotem do Widoku - czy powinno to przejść ponownie przez Model, czy też sterownik powinien po prostu wysłać go z powrotem do Widoku?

Powinieneś mieć ustawiony protokół, w którym widok niekoniecznie wie, co będzie dalej, dopóki kontroler tego nie powie. Na jakim ekranie wyświetlasz je po tym, jak użytkownik uderzy ten przycisk? Widok może nie wiedzieć, a Administrator może nie wiedzieć, dopóki nie spojrzy na dane, które właśnie otrzymał. Może to być „Przejdź do tego innego ekranu, zgodnie z oczekiwaniami” lub „Pozostań na tym ekranie i wyświetl ten komunikat o błędzie”.

Z mojego doświadczenia wynika, że ​​bezpośrednia komunikacja między modelem a widokiem powinna być bardzo, bardzo ograniczona, a widok nie powinien bezpośrednio zmieniać żadnych danych modelu; to powinno być zadanie kontrolera.

Jeśli sprawdzanie poprawności odbywa się w widoku, co mam umieścić w kontrolerze?

Patrz wyżej; prawdziwa walidacja powinna odbywać się w kontrolerze. I mam nadzieję, że masz już pewne pojęcie o tym, co powinno zostać wprowadzone do Kontrolera. :-)

Warto zauważyć, że wszystko może być trochę rozmyte wokół krawędzi; podobnie jak w przypadku większości tak złożonych zagadnień, jak inżynieria oprogramowania, będzie wiele wezwań do osądu. Po prostu dokonaj najlepszego osądu, staraj się zachować spójność w tej aplikacji i spróbuj zastosować zdobyte lekcje do następnego projektu.

BlairHippo
źródło
7

Kontroler jest naprawdę częścią widoku. Jego zadaniem jest ustalenie, które usługi są potrzebne do spełnienia żądania, niemaralne wartości z Widoku do obiektów wymaganych przez interfejs usługi, określenie następnego Widoku i przekierowanie odpowiedzi z powrotem do formularza, z którego będzie mógł korzystać następny Widok . Obsługuje również wszelkie zgłoszone wyjątki i przekształca je w Widoki, które użytkownicy mogą zrozumieć.

Warstwa serwisowa to rzecz, która zna przypadki użycia, jednostki pracy i obiekty modelu. Kontroler będzie inny dla każdego typu widoku - nie będziesz mieć tego samego kontrolera dla interfejsu użytkownika na komputerze, w przeglądarce, Flex lub na urządzeniach mobilnych. Więc mówię, że to naprawdę część interfejsu użytkownika.

Zorientowane na usługi: tam właśnie wykonywana jest praca.

duffymo
źródło
3

Kontroler służy przede wszystkim do koordynacji między widokiem a modelem.

Niestety czasem kończy się to mieszaniem z widokiem - w małych aplikacjach nie jest to jednak takie złe.

Sugeruję umieszczenie:

public void actionPerformed(ActionEvent e)

w kontrolerze. Następnie twój detektor akcji w twoim widoku powinien przekazać kontrolerowi.

Jeśli chodzi o część dotyczącą walidacji, możesz umieścić ją w widoku lub kontrolerze, osobiście uważam, że należy ona do kontrolera.

Zdecydowanie poleciłbym rzucić okiem na widok pasywny i nadzorowanie prezentera (który jest zasadniczo tym, na co podzielony jest prezenter widoków modelu - przynajmniej przez Fowlera). Widzieć:

http://www.martinfowler.com/eaaDev/PassiveScreen.html

http://www.martinfowler.com/eaaDev/SupervisingPresenter.html

Jon
źródło
3

Oto ogólna zasada, której używam: jeśli jest to procedura, której będę używał specjalnie do akcji na tej stronie, należy ona do kontrolera, a nie do modelu. Model powinien zapewniać jedynie spójną abstrakcję przechowywania danych.

Wymyśliłem to po pracy z dużą aplikacją webową napisaną przez programistów, którzy myśleli, że rozumieli MVC, ale tak naprawdę nie. Ich „kontrolery” są zredukowane do ośmiu linii wywoływania metod klasy statycznej, które zwykle są wywoływane nigdzie indziej: - / czyniąc swoje modele niewiele więcej niż sposoby tworzenia przestrzeni nazw. Poprawne przeredagowanie tego powoduje trzy rzeczy: przesuwa cały kod SQL do warstwy dostępu do danych (inaczej model), sprawia, że ​​kod kontrolera jest nieco bardziej szczegółowy, ale o wiele bardziej zrozumiały, i redukuje stare pliki „modelowe” do zera. :-)

staticsan
źródło
1

zauważ także, że każdy widżet Swing może być uznany za zawierający trzy komponenty MVC: każdy ma Model (tj. ButtonModel), Widok (BasicButtonUI) i Kontrolę (sam JButton).

akf
źródło
1

Masz zasadniczo rację co do tego, co umieściłeś w kontrolerze. Jest to jedyny sposób, w jaki Model powinien oddziaływać z widokiem. Wykonaną akcję można umieścić w widoku, ale rzeczywistą funkcjonalność można umieścić w innej klasie, która działałaby jako kontroler. Jeśli masz zamiar to zrobić, polecam przyjrzeć się wzorowi poleceń, który jest sposobem na wyodrębnienie wszystkich poleceń, które mają ten sam odbiornik. Przepraszam za dygresję.

W każdym razie właściwa implementacja MVC będzie mieć tylko następujące interakcje: Model -> Widok Widok -> Kontroler kontrolera -> Widok

Jedynym miejscem, w którym może wystąpić kolejna interakcja, jest użycie obserwatora do zaktualizowania widoku, wówczas widok będzie musiał poprosić kontrolera o informacje, których potrzebuje.

mnuzzo
źródło
0

Jak rozumiem, Kontroler tłumaczy z akcji interfejsu użytkownika na akcje na poziomie aplikacji. Na przykład w grze wideo kontroler może tłumaczyć „przesunął mysz tak wiele pikseli” na „chce spojrzeć w takim i takim kierunku. W aplikacji CRUD tłumaczenie może być„ kliknięte na taki i taki przycisk ”, aby „wydrukuj to”, ale koncepcja jest taka sama.

David Seiler
źródło
0

Robimy to w ten sposób, używając Kontrolerów głównie do obsługi i reagowania na sterowane przez użytkownika dane wejściowe / akcje (i _Logic dla wszystkiego innego, z wyjątkiem widoku, danych i oczywistych rzeczy _Model):

(1) (odpowiedź, reakcja - co „robi” aplikacja internetowa w odpowiedzi na użytkownika) Blog_Controller

-> main ()

-> handleSubmit_AddNewCustomer ()

-> VerifyUser_HasProperAuth ()

(2) (logika „biznesowa”, co i jak „myśli” aplikacja internetowa)) Blog_Logic

-> sanityCheck_AddNewCustomer ()

-> handleUsernameChange ()

-> sendEmail_NotifyRequestedUpdate ()

(3) (widoki, portale, jak „aplikacja” się pojawia)) Blog_View

-> genWelcome ()

-> genForm_AddNewBlogEntry ()

-> genPage_DataEntryForm ()

(4) (tylko obiekt danych, uzyskany w _ construct () każdej klasy Blog *, używany do przechowywania wszystkich danych aplikacji internetowej / pamięci w jednym obiekcie) Blog_Meta

(5) (podstawowa warstwa danych, odczytuje / zapisuje do baz danych) Blog_Model

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> loadData ()

Czasami mylimy się trochę, gdzie umieścić metodę, w C lub L. Ale Model jest solidny jak kamień, krystalicznie czysty, a ponieważ wszystkie dane w pamięci znajdują się w _Meta, nie jest to również oczywiste . Nawiasem mówiąc, naszym największym krokiem naprzód było przyjęcie _Meta, ponieważ to oczyściło całą resztę z różnych obiektów _C, _L i _Model, dzięki czemu wszystko było mentalnie łatwe do zarządzania, a za jednym zamachem dało nam to, co jest nazywany „Wstrzykiwaniem zależności” lub sposobem na obejście całego środowiska wraz ze wszystkimi danymi (którego zaletą jest łatwe stworzenie środowiska „testowego”).

FYA
źródło