Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [MVC] Widok nie odwołujący się bezpośrednio do modelu?
Forum PHP.pl > Forum > PHP > Object-oriented programming
MacDada
Hej,

przeczytałem kupę tematów o MVC na forum i zastanawia mnie tu jedna rzecz. Większość osób pokazuje schemat działania aplikacji jako taki:
1.) Kontroler rozpoznaje co użytkownik chce zrobić (np wyświetlić listę użytkowników).
2.) Kontroler odpala wtedy odpowiedni widok.
3.) Widok odpala model.
4.) Model przerabia co tam potrzebuje i zwraca dane do widoku.
5.) Widok prezentuje dane.

Co mnie dziwi to fakt, że w ten sposób jeśli zmienię model, to muszę ingerować też w pliki widoku. Jest on uzależniony od konkretnego modelu. No i widok bezpośrednio współpracuje z modelem, a kontroler pełni funkcję jedynie routera.

Czy w takim razie nie lepszy byłby schemat taki?
1.) Kontroler rozpoznaje co użytkownik chce zrobić (np wyświetlić listę użytkowników).
2.) Kontroler wybiera model i go odpala.
3.) Model przerabia co tam potrzebuje i zwraca dane do kontrolera.
4.) Kontroler wybiera widok i przekazuje mu dane od modelu.
5.) Widok wyświetla dane.

W ten sposób Kontroler rzeczywiście pełni funkcję pośredniczącą między warstwą danych i działań, a warstwą prezentacyjną. Mogę stworzyć różne widoki do reprezentowania tego samego typu danych, a jednocześnie mogę mieć wiele źródeł danych, które będą wyświetlane przez ten sam widok.

Co więcej upraszcza to schemat akcji, który może wyglądać tak:
1.) Kontroler rozpoznaje, co użytkownik chce zrobić (np dodać nowego użytkownika do bazy).
2.) Kontroler wybiera model i go odpala.
3.) Model przerabia co tam potrzebuje i zwraca dane do kontrolera (np kod błędu, kod sukcesu, itd.)
4.) Kontroler wybiera widok i przekazuje mu dane od modelu.
5.) Widok wyświetla dane.

Voila! Okazuje się w ten sposób, że model jest równoważny akcji. Model służy wszystkim operacjom na danych, kontroler służy rozpoznawaniu tego co chce użytkownik i tego co chce model, a widok służy do prezentowania danych przekazanych mu przez kontroler. Co więcej, jeśli widok zwróci błąd, to kontroler znów rozpoznaje co się dzieje i odpala odpowiedni model jako reakcję, np logger. Pure MVC?

Czy może jednak w takim podejściu tkwi jakiś błąd?
pozdr.
Quantum
Kontroler musi mieć dostęp do modelu - to oczywiste, ale żeby było zgodnie z MVC, widok także powinien mieć do niego dostęp. Takiego rozwiązania używamy tylko wtedy gdy jest dla nas "komfortowe". Przykład: menu renderowane jest z danych pobranych z bazy, nie będziemy chyba dołączać do każdego kontrolera kodu odpowiadającego za przypisanie menu pobranego przez model do zmiennej w widoku winksmiley.jpg (pomijam tu globalne przypisywanie zmiennych do widoków).
Zyx
Wtedy jeśli zmienisz model, zamiast widoku będziesz musiał modyfikować kontroler smile.gif. Poza tym to już wtedy nie będzie MVC, ale jeden z wzorców pochodnych. W MVP jedno z założeń mówi, że rola modelu i widoku jest zmniejszona na rzecz przypakowanego kontrolera zwanego tam prezenterem, który m.in. pośredniczy w komunikacji. Tyle jeśli chodzi o część teoretyczną, teraz praktyka:

Tak, jak mówisz, robione jest w zdecydowanej większości dostępnych frameworków. I to, czy jest to podejście wygodniejsze czy nie, to już trzeba programistów pytać. Na pewno w części zastosowań lepiej się ono sprawdzi, jednak w innych lepszy będzie MVC. Ja osobiście jestem zwolennikiem trzymania się oryginału bez żadnych udziwnień, ponieważ bardzo często już miałem do czynienia z mocno przerośniętymi kontrolerami, które robiły dosłownie wszystko, tylko nie to, do czego zostały stworzone. Ponadto zapominasz o pewnej rzeczy, jak ustandaryzowany interfejs. Nie wyobrażam sobie sytuacji, by modele nie trzymały się jakiejś konwencji. Dzięki temu niedawno udało mi się zaprojektować... kontroler CRUD ogólnego przeznaczenia oraz zestaw widoków do niego. Może z nim współpracować każdy model, który implementuje odpowiednie interfejsy, przez co de facto napisanie panelu sprowadza się do zrobienia modelu, ponieważ kod kontrolera liczy sobie 7 linijek, z czego 5 to deklaracje klasy, metod i nawiasy klamrowe smile.gif.
zelu
Od razu mówię, że nie mam jakiegoś strasznie dużego doświadczenia we frameworkach, ale wydaje mi się, że drugie podejście (wszystko przechodzi przez kontroler) jest sensowniejsze i bardziej intuicyjne. Piszesz Zyx, że w drugim podejściu kontroler robi wiele rzeczy, których nie powinien. A co, kiedy w pierwszym schemacie (typowy MVC) widok pobiera dane z Modelu i okazuje się, że Model zwrócił kod błędu. Teraz to widok zaczyna robić zbyt wiele zbędnych rzeczy, ponieważ musi sprawdzać kod błędu i zawiera w sobie niepotrzebne instrukcje if, przez co traci na przejrzystości kodu. A w podejściu drugim wywołujemy model i mamy prosty zestaw instrukcji warunkowych z renderowaniem widoków. Przejrzystość kodu znacznie na tym zyskuje.
A dodatkowo co z sytuacją o której pisał MacDada: zwracanie różnych typów widoków (HTML, JSON, XML). Wtedy tylko raz tworzymy odwołanie do danego modelu i ponownie dzięki kilku prostym instrukcjom if wyświetlamy odpowiedni widok.

Oczywiście tak jak powiedziałem, nie mam dużego doświadczenia we frameworkach, dlatego byłbym wdzięczny za ewentualne skorygowanie mojego myślenia winksmiley.jpg


Pozdrawiam
Zyx
Jakie setki ifów? Słyszał waść o wyjątkach? smile.gif Rzecz jest prosta: widok nie łapie wyjątków modelu, dzięki czemu może odebrać je kontroler i zareagować, kierując do standardowego systemu obsługi błędów modelu.

Jeśli chodzi o kwestię różnych typów widoków, umiejscowienie komunikacji niewiele zmienia. Podam Ci przykład w pseudokodzie:

  1. public function someAction()
  2. {
  3. switch($request->view)
  4. {
  5. case 'html':
  6. $view = new View_Something_Html;
  7. break;
  8. case 'xml':
  9. $view = new View_Something_Xml;
  10. break;
  11. case 'json':
  12. $view = new View_Something_Json;
  13. break;
  14. }
  15. $view->addModel('datasource', new Some_Model);
  16. return $view;
  17. } // end someAction();


Pamiętaj, że widok to nie tylko kod HTML (szablony), ale też logika prezentacji. Stronicowanie wyników ma sens w przypadku HTML-a, ale może nie mieć w formacie JSON lub PDF. Obsługa stronicowania to zadanie widoku - dzięki powyższemu podejściu, gdzie widok sam sobie pobiera dane, kontroler nie musi nawet wiedzieć, co dokładnie jest mu potrzebne do wykonania swojego zadania. Jeśli widok nie komunikuje się z modelem, kod ten musi wylądować w kontrolerze i wtedy nagle tu się pojawia mnóstwo ifów, a kod kontrolera staje się długi i przeładowany.

Ponadto warto zwrócić uwagę, że da się stworzyć tzw. uniwersalne widoki wielokrotnego przeznaczenia. Jest to coś, co ciężko uświadczyć we frameworkach, które warstwę widoku zredukowały do systemu szablonów. Mam sobie uniwersalny widok do wyświetlania listy elementów, który ma już wbudowaną obsługę stronicowania, sortowania, filtrowania i co tam sobie jeszcze wymarzysz. Jeśli wszystkie dane idą przez kontroler, a ja będę chciał gdzieś sobie zrobić listę elementów, muszę zrobić Ctrl+C i Ctrl+V całego kodu, a następnie go popoprawiać, lub też obudować sztuczną atrapą, która sprowadzi nas do MVC. Tymczasem ja sobie robię:

  1. public function displayMeSomethingAction()
  2. {
  3. $model = new Model_Something;
  4. $model->id = $request->getParam('id', null);
  5. $view = new View_List;
  6. $view->controllerIdentity = $this->getControllerIdentity(); // Dzięki temu widok może prawidłowo ustawić np. breadcrumbs i tytuł
  7. $view->addModel('list', $model);
  8.  
  9. return $view;
  10. } // end displayMeSomethingAction();


I co? 6 linijek, a ja mam listę elementów modelu z sortowaniem, stronicowaniem, filtrowaniem i wszystkim, co sobie tylko model zażyczy. Jeśli tę samą sztuczkę chcę powtórzyć w innym miejscu, mogę to od razu zrobić, bo jedyne, co tak naprawdę muszę zrobić, to wprowadzić model do gotowego widoku. Kod kontrolera jest krótki i przejrzysty, dzięki czemu po dodaniu uwierzytelniania i kontroli uprawnień wciąż nadaje się do czytania.

PS. Jeśli chcesz poeksperymentować z tego typu rzeczami, to obejrzyj sobie mój eksperymentalny framework stworzony do tego celu: http://github.com/zyxist/Trinity . Kod jest publicznie dostępny i możesz namacalnie sprawdzić, jak to działa w praktyce.
zelu
No i tutaj wychodzi brak doświadczenia winksmiley.jpg

Faktycznie teraz ma to dla mnie dużo większy sens i właśnie o taką odpowiedź mi chodziło smile.gif Tak więc ode mnie duże ukłony i dziękuję winksmiley.jpg

Trzeba jedynie zmienić trochę sposób myślenia i przystosować się do takiego podejścia, chociaż moim zdaniem ilość kodu będzie porównywalna w obu przypadkach.

Niemniej podejrzewam, że dla wielu osób nadal podejście numer 2 jest bardziej intuicyjne i stąd takie, a nie inne zachowanie współczesnych frameworków.


Jeszcze raz dzięki i pozdrawiam
Zyx
Również dziękuję, bowiem sam też przy okazji odpowiadania znalazłem rozwiązanie jednego zagadnienia, które mnie nurtowało smile.gif.

Zgadzam się, że MVC wymaga od programisty pewnego przyzwyczajenia, tym bardziej że nie ma się za bardzo na czym wzorować. Z tego, co już zdążyłem zauważyć, efektywne wykorzystanie MVC zależy w dużej mierze od zdyscyplinowania programisty z dwóch powodów. Pierwszy to kwestia interfejsów, które należy dobrze zdefiniować i się ich trzymać. Hipotetyczny framework MVC powinien oczywiście dostarczać szereg gotowych interfejsów do typowych zadań, ale przy bardziej egzotycznych potrzebach programista będzie już musiał stworzyć coś własnego. Drugi powód to umiejętność dostrzegania schematów i pisania takiego kodu, by nadawał się on do wielokrotnego użycia. Można pracować i bez tego, ale jak to jest z wzorcami - jeden programista zrobi z nich cudo, a drugi skopie dokumentnie smile.gif.

Podejście nr 2 jest o tyle intuicyjne, że de facto nie różni się ono w działaniu od skryptów pisanych sposobami gospodarczymi smile.gif. Zauważ, że jak pisałeś kiedyś jakieś proste stronki, miałeś schemat: pobierz dane z bazy i przekaż je do systemu szablonów. Co prawda pobierałeś tam dane bezpośrednio z bazy, ale schemat postępowania był dokładnie taki sam i wiele rozwiązań można przenieść na grunt frameworków niemal bezpośrednio. Wg mnie takie uproszczenie dobrze działa dla aplikacji schematycznych, natomiast staje się mocno problematyczne przy bardziej nietypowych wymaganiach (np. modularny CMS, gdzie definicja strony, zamiast być zakodowana na sztywno, musi być ładowana z bazy, albo warstwa modelu jest bardzo skomplikowana).
MacDada
OK, czyli mechanizm, który opisałem to MVP. Próbowałem przeanalizować kod z git-huba z „prawdziwym MVC”, ale po kilku plikach doszedłem do wniosku, że nie potrzebuję w swoich projektach aż takiej komplikacji. MVP w zupełności mi na razie odpowiada smile.gif

W każdym razie dzięki Zyx, bo widzę, że nie pierwszy raz się produkowałeś na ten temat... winksmiley.jpg
http://www.zyxist.com/en/archives/16
Crozin
Cytat
doszedłem do wniosku, że nie potrzebuję w swoich projektach aż takiej komplikacji
Jeżeli analizowałeś projekt Trinity to wiedz, że tam masz masę "syfu" niezwiązanego z samą implementacją MVC.

IMO spróbuj sobie zrobić jakąś prostą aplikację nawet typu blog opartą na prawdziwym MVC (no, takim troszkę zmodyfikowanym na potrzeby środowiska HTTP, bo w nim ciężko o to by Model powiadamiał Widok o tym, że ma się on odświeżyć :]). Napewno zyskasz cenne doświadczenie + będziesz mógł rzetelnie ocenić czy MVC czy MVP bardziej pasuje do Twoich wymagań.
Cysiaczek
Z tą nomenklaturą to też ostrożnie. Gdy szukałem po necie o różnicach MVC i MVP, to też nie wyszło tak pięknie jak opisuje Zyx. Po wizycie na jednej, czy dwóch stronach w ogóle straciłem rozeznanie sciana.gif
MacDada
Pod względem teoretycznym zgadzam z Zyx'em. Wystarczy spojrzeć na diagram MVC na Wikipedii i wyraźnie widać, że Widok także może mieć bezpośredni dostęp do modelu.

Stąd doszedłem do wniosku, że w praktyce zamiast MVC lepiej zrobić MVTC, czyli Model View Template Controller. W ten sposób mamy Kontroler, który odpala model i odpowiedni widok, widok odpala sobie w razie potrzeby model, a na koniec zbiera to co chce do kupy i przekazuje do Szablonu. W ten sposób wilk syty i owca cała, tzn Graficy bawią się jedynie na Szablonie, a jednocześnie Widok nie jest tylko Szablonem, ale zawiera także logikę Widoku w postaci paginatora, itp.

Choć na razie to czysta teoria, bo takiego podejścia nie wypróbowałem i nie widziałem winksmiley.jpg


EDIT:
W mojej wizji kontrolerów byłoby wiele.
Przykład oklepany - blog winksmiley.jpg

  • Kontroler (router) główny rozpoznaje, że użytkownik chce wyświetlić wpis na blogu. Odpala Widok zajmujący się wyświetlaniem wpisu.
  • Widok wyświetlania wpisu odpala sobie Kontroler budowania menu. On rozpoznaje jakie menu chcemy wyświetlić (może być kilka rodzajów menu, kilka sposobów jego budowy) i odpala odpowiedni Widok.
  • Widok ten pobiera dane z Modelu opisującego wybrany rodzaj menu i go wyświetla.
  • Wracamy do Widoku wyświetlania wpisu. Zbudował on sobie i wyświetlił menu, więc pobiera sobie dane z Modelu zawierającego wpisy i wyświetla główną treść.
  • Oprócz tego Widok wyświetlający wpis może chcieć wyświetlić reklamy, więc odpala sobie Kontroler reklam. Ten działa podobnie do Kontrolera menu.


Jak widać generalnie działałoby to tak, że Kontroler nadrzędny odpala Widok, który korzysta z podrzędnych Kontrolerów i odpowiedniego Modelu, by zbudować sobie dynamiczną zawartość. Taka jakby hierarchia. Co dziwne, w ten sposób Kontrolerzy nie korzystają z Modelów, bo... nie potrzebują? Nie wiem po co miałyby to robić.

Zalety tego rozwiązania (oprócz samego oddzielenia warstwy prezentacji od treści) widzę dwie:
  • aplikację można podzielić na moduły/pluginy. Każdy moduł zbudowany byłby jako MVC. Inne moduły i moduł główny odpalałyby Kontroler modułu, mówiąc mu czego oczekują, a ten by to realizował;
  • tak jak pisałem wcześniej, Widoki korzystałyby z Szablonów, więc graficy nie bawią się logiką Widoku, a jedynie dizajnem Szablonu, a co więcej jeden Szablon mógłby być wykorzystany w różnych Widokach.
marcio
Cytat(MacDada @ 29.07.2010, 01:00:57 ) *
Pod względem teoretycznym zgadzam z Zyx'em. Wystarczy spojrzeć na diagram MVC na Wikipedii i wyraźnie widać, że Widok także może mieć bezpośredni dostęp do modelu.

Stąd doszedłem do wniosku, że w praktyce zamiast MVC lepiej zrobić MVTC, czyli Model View Template Controller. W ten sposób mamy Kontroler, który odpala model i odpowiedni widok, widok odpala sobie w razie potrzeby model, a na koniec zbiera to co chce do kupy i przekazuje do Szablonu. W ten sposób wilk syty i owca cała, tzn Graficy bawią się jedynie na Szablonie, a jednocześnie Widok nie jest tylko Szablonem, ale zawiera także logikę Widoku w postaci paginatora, itp.

Choć na razie to czysta teoria, bo takiego podejścia nie wypróbowałem i nie widziałem winksmiley.jpg


EDIT:
W mojej wizji kontrolerów byłoby wiele.
Przykład oklepany - blog winksmiley.jpg

  • Kontroler (router) główny rozpoznaje, że użytkownik chce wyświetlić wpis na blogu. Odpala Widok zajmujący się wyświetlaniem wpisu.
  • Widok wyświetlania wpisu odpala sobie Kontroler budowania menu. On rozpoznaje jakie menu chcemy wyświetlić (może być kilka rodzajów menu, kilka sposobów jego budowy) i odpala odpowiedni Widok.
  • Widok ten pobiera dane z Modelu opisującego wybrany rodzaj menu i go wyświetla.
  • Wracamy do Widoku wyświetlania wpisu. Zbudował on sobie i wyświetlił menu, więc pobiera sobie dane z Modelu zawierającego wpisy i wyświetla główną treść.
  • Oprócz tego Widok wyświetlający wpis może chcieć wyświetlić reklamy, więc odpala sobie Kontroler reklam. Ten działa podobnie do Kontrolera menu.


Jak widać generalnie działałoby to tak, że Kontroler nadrzędny odpala Widok, który korzysta z podrzędnych Kontrolerów i odpowiedniego Modelu, by zbudować sobie dynamiczną zawartość. Taka jakby hierarchia. Co dziwne, w ten sposób Kontrolerzy nie korzystają z Modelów, bo... nie potrzebują? Nie wiem po co miałyby to robić.

Zalety tego rozwiązania (oprócz samego oddzielenia warstwy prezentacji od treści) widzę dwie:
  • aplikację można podzielić na moduły/pluginy. Każdy moduł zbudowany byłby jako MVC. Inne moduły i moduł główny odpalałyby Kontroler modułu, mówiąc mu czego oczekują, a ten by to realizował;
  • tak jak pisałem wcześniej, Widoki korzystałyby z Szablonów, więc graficy nie bawią się logiką Widoku, a jedynie dizajnem Szablonu, a co więcej jeden Szablon mógłby być wykorzystany w różnych Widokach.

To o czym mowisz to fron controller i block controller czy jakos tak czyli jeden glowny szbalon dla calej strony do tego jeden kontroler i model ktory pobiera w jakie miejsce do glownego szablonu maja byc podpiete moduly/pluginy/widgety czy co tam chcesz.Jak juz sam mowiles kazdy modul np ma swoj model,kontroler i szablon/template...

Ja mam tak w moim fw jest mi z tym wygodnie i w ogole wszystko ladnie hula, wrzuce gdzies na jakiegos rapida zeby mozna bylo sobie zobaczyc pisane dosyc dawno wiec kod troche lichy teraz napisalbym bym to przynajmniej x2 lepiej :]

Jedyne co to cala logika jest w kontrolerze bo "widok" u mnie to zwykly szablon/templare nie klasa widoku ktora odpala szablon ;]


P.S lap jak chcesz: http://www.sendspace.pl/file/1e498eb450c7b4c35fa1098
Crozin
@MacDada: To co opisałeś to tzw. HMVC (tj. nie MVC, ale na literce H się teraz skupiamy). H jak hierarchiczny.
MacDada
Cytat(Crozin @ 29.07.2010, 20:47:14 ) *
@MacDada: To co opisałeś to tzw. HMVC (tzw. nie MVC, ale na literce H się teraz skupiamy). H jak hierarchiczny.
Coś tak czułem, że podejrzane by było, gdyby nikt wcześniej na to nie wpadł haha.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.