Wprowadzenie do MVC

Transkrypt

Wprowadzenie do MVC
Wprowadzenie do MVC
Maciej 'hawk' Jarzębski
1.
2.
3.
4.
5.
6.
Co to jest MVC?
Rys historyczny
Krótko o wzorcach projektowych
Po co używać MVC?
Kiedy używać MVC?
Elementy MVC
6.1. Model
6.2. Widok
6.3. Sterownik
7. Obsługa żądania
8. Istniejące rozwiązania
1. Co to jest MVC?
Zacznijmy od rozszyfrowania nazwy: skrót MVC pochodzi od słów Model - Widok
- Sterownik (ang. Model - View - Controller). MVC zdobywa coraz większą
popularność i przedstawiany jest jako dobra architektura aplikacji internetowej.
W tym artykule mam nadzieję wyjaśnić, na czym polega MVC i dlaczego warto
się tym bliżej zainteresować.
MVC jest zorientowanym obiektowo wzorcem projektowym. Aplikacja napisana
zgodnie z MVC zawiera klasy implementujące logikę biznesową (jak i gdzie dane
są przechowywane, kto ma do nich dostęp, jak dane są przetwarzane) w
"modelach", logikę prezentacyjną (jak dane pochodzące z modeli mają zostać
wyświetlone) w "widokach", a logika sterująca całą aplikacją zawarta jest w
"kontrolerze".
2. Rys historyczny
MVC nie jest pomysłem nowym. Wymyślony został w laboratoriach PARC (Palo
Alto Research Centre) firmy Xerox (tych samych, którym zawdzięczamy m. in.
myszki i systemy okienkowe) już w latach siedemdziesiątych. Wtedy oczywiście
nikt nawet nie myślał o wykorzystaniu wzorca do aplikacji internetowych. MVC
został wykorzystany do zaprojektowania interfejsu użytkownika w języku
Smalltalk, stworzonym zresztą również przez PARC. To, co nas jednak interesuje,
to zastosowanie MVC w aplikacjach internetowych. Tego kroku dokonał Sun na
potrzeby Javy.
Java pełni tutaj rolę szczególną również dlatego, że najbardziej znana
implementacja MVC powstała właśnie dla Javy. Tym projektem jest Struts,
stworzony przez Apache Jakarta Project. Struts nie jest jedyną słuszną
implementacją MVC, ale na pewno najbardziej popularną i w pewnym sensie
referencyjną.
http://www.php.pl
Strona 1 z 8
W rozważaniach nad MVC przewijają się również pojęcia Model 1 i Model 2.
Pochodzą one ze specyfikacji JSP i J2EE. Model 1 był podejściem, w którym
logika biznesowa była umieszczona w modelu, ale widok rozrzucony był po
poszczególnych stronach, bez centralnego kontrolera. Model 2 natomiast jest w
zasadzie tym samym co MVC, dlatego w kontekście aplikacji internetowych te
dwa pojęcia mogą być używane zamiennie.
3. Krótko o wzorcach projektowych
MVC jest, jak napisałem, wzorcem projektowym. Dla osób, które nie miały
jeszcze styczności z wzorcami, postaram się krótko wyjaśnić, w czym rzecz.
Wzorzec projektowy jest kolekcją obiektów i powiązań między nimi, wymyśloną,
opisaną i wielokrotnie sprawdzoną, przeznaczoną do efektywnego rozwiązywania
pewnego problemu. Jeżeli problem, który musisz rozwiązać, jest podobny do
problemu rozwiązywanego przez jeden ze znanych wzorców, możesz
zaoszczędzić sporo pracy wykorzystując ten wzorzec w swoim kodzie. Wzorzec
nie jest gotowym kodem. Kod w dalszym ciągu musisz sam napisać, ale będziesz
mógł oprzeć się na sprawdzonym i dobrze udokumentowanym rozwiązaniu.
Wzorców projektowych jest wiele, tak jak wiele jest powtarzających się
problemów, które wzorce rozwiązują. Model - Widok - Sterownik jest właśnie
jednym z takich wzorców, który szczególnie nadaje się do rozwiązywania
szczególnej klasy problemów: jak dobrze zaprojektować aplikację internetową.
4. Po co używać MVC?
To, że MVC jest wzorcem projektowym, samo w sobie nie oznacza, że mamy go
stosować pisząc kolejną aplikację w PHP. Powodem jest to, że MVC idealnie
wpasowuje się w specyfikę zastosowań. Każde żądanie nowej strony jest
interakcją użytkownika z systemem i musi zostać jakoś przetworzone (kontroler).
Większość aplikacji musi przechowywać dane w sposób trwały - najczęściej w
bazie danych (model). I wreszcie dane te muszą zostać wyświetlone w jakiś
sposób, lub raczej na wiele różnych sposobów (widok).
Model - Widok - Sterownik odpowiada tym podstawowym potrzebom. Każda z
części składowych wzorca ma ściśle zdefiniowane zadanie, jak na rysunku:
http://www.php.pl
Strona 2 z 8
Podstawową zaletą MVC jest modyfikowalność. Podstawowym problemem w
utrzymywaniu skomplikowanych aplikacji jest konieczność wprowadzania zmian.
Byle jak napisana aplikacja jest w zasadzie niemodyfikowalna. Każda zmiana
może generować nowe błędy. Odpowiedzią na to jest - ogólnie rzecz biorąc podejście obiektowe i hermetyzacja. MVC właśnie hermetyzuje poszczególne
części aplikacji.
Jeżeli zachodzi konieczność zmiany struktury bazy danych, lub wymiany całej
bazy, wystarczy zmienić kod modelu. Mamy pewność, że nie będzie to miało
wpływu na inne części aplikacji.
Jeżeli trzeba zmienić wygląd stron, lub zmienić technologię generowania wyników
(np. przejść na szablony w aplikacji, która ich na razie nie wykorzystuje), zmiany
ograniczają się do klas widoku.
5. Kiedy używać MVC?
Żadne rozwiązanie nie jest uniwersalne. Podobnie MVC nie nadaje się do każdej
aplikacji. Jeżeli mamy do czynienia z prostym skryptem wytwarzanym metodą
"napisz i zapomnij", zalety MVC nie będą równoważyć kosztów. Zyski wynikające
ze stosowania tego wzorca - jak zresztą i innych "dobrych praktyk
programowania" - są znaczące gdy:
•
•
•
•
aplikacja jest duża i skomplikowana
aplikację trzeba będzie utrzymywać i dostosowywać do nowych potrzeb
w wytwarzanie zaangażowanych jest wiele ludzi, którym trzeba wyznaczyć
obszary kompetencji
zamierzamy wykorzystać fragmenty aplikacji (np. klasy modelu) w innych
projektach
6. Elementy MVC
Skoro wiemy już, czym jest MVC i dlaczego stosowany jest do tworzenia aplikacji
internetowych, przyjrzyjmy się bliżej poszczególnym jego składnikom.
6.1. Model
Model jest częścią MVC, odpowiedzialną za tzw. logikę biznesową. Termin logika
biznesowa odnosi się do funkcjonalności związanej ze sposobem, w jaki aplikacja
przechowuje dane.
Model powinien być jedyną częścią aplikacji, która przechowuje dane w sposób
trwały. Sposób przechowywania tych danych jest tutaj zupełnie obojętny - może
to być baza danych, pliki tekstowe, Web Services, itd. Ważne jest, aby szczegóły
implementacyjne związane ze sposobem, w jaki Twój model przechowuje dane,
były ukryte przed resztą aplikacji. Model powinien dostarczać innym
komponentom spójnego interfejsu do przetwarzania tych danych i ukrywać
implementację - np. zapytania SQL.
http://www.php.pl
Strona 3 z 8
Dobrym sposobem implementacji modelu jest utworzenie oddzielnej klasy dla
każdego logicznego obiektu. Typowe przykłady to:
•
•
•
Użytkownicy
Artykuły
Koszyk (w sklepie internetowym)
Taka klasa może wykorzystywać wzorzec Singleton i powinna zawierać metody
pozwalające na wykonywanie wszystkich operacji na związanych z nią danych,
wymaganych przez resztę aplikacji.
Najczęściej model będzie przechowywał informacje w bazie danych. W takim
przypadku przeważnie jedna klasa modelu odpowiada jednej tabeli w bazie
danych, lub grupie ściśle powiązanych tabel. W przypadku konieczności zmiany
jednej z tabeli, zmiany w kodzie PHP ograniczają się do modelu, a nawet do
jednej klasy modelu. Dobrze zaprojektowany model powinien być odporny na
takie wewnętrzne zmiany, tzn. zmiany w strukturze bazy danych nie powinny
wpływać na interfejs, z którego korzystają pozostałe części aplikacji.
Dobrze zaprojektowany model powinien również być przenośny. Jeżeli mamy
zaimplementowane klasy modelu służące do obsługi użytkowników (logowanie,
administracja, itd.), możemy łatwo przenieść ten kod do innej aplikacji, która
również wymaga takiej funkcjonalności. I to nawet nie metodą "kopiuj - wklej",
tylko przenosząc bez zmian gotowe klasy.
6.2. Widok
Widok jest częścią aplikacji odpowiedzialną za wyświetlanie danych
użytkownikom. Innymi słowy, widok odpowiada za prezentację. W przypadku
aplikacji internetowych, najczęściej używanym formatem wyjściowym jest
oczywiście HTML, ale widok nie musi ograniczać się tylko do niego. Równie
dobrze aplikacja może prezentować wyniki w postaci XML, WML, obrazków,
plików PDF i w wielu innych formatach. PHP ma wyjątkowo bogate możliwości w
tym zakresie: szablony, XSLT, biblioteki do generacji obrazków, PDF lub Flasha...
Widok powinien wykorzystywać model do pobrania danych, które będą
wyświetlone. Typowo, widok powinien utworzyć instancje klas modelu i wywołać
metody odpowiedzialne za pobranie odpowiednich danych. Tym, czego należy się
wystrzegać w widoku, jest modyfikacja danych. Widok nie powinien zmieniać w
żaden sposób stanu aplikacji. Powinien np. wyświetlać listę użytkowników, ale nie
powinien ich dodawać ani usuwać.
Widok jest prawdopodobnie łatwy do zrozumienia dla osób używających
szablonów. W obu przypadkach celem jest oddzielenie logiki prezentacyjnej od
logiki biznesowej. Należy jednak pamiętać, że widok nie jest szablonem (lub
raczej zbiorem szablonów). Widok to przede wszystkim kod, który "wyciąga" z
modelu potrzebne dane, natomiast nic nie stoi na przeszkodzie, aby widok
wykorzystywał wewnętrznie szablony.
Widok również może być implementowany w postaci wielu klas, po jednej na
każdą stronę, jeden dokument PDF, jeden plik XML, itd. W dobrze
http://www.php.pl
Strona 4 z 8
zaprojektowanej aplikacji widok powinien być "wymienny": zmiana formatu
wyświetlanych danych (np. z HTML do PDF) powinna być osiągalna przez prostą
wymianę klas widoku. Ma to obecnie coraz większe znaczenie, ponieważ od
aplikacji zaczynamy wymagać obsługi formatów wyjściowych innych niż
tradycyjny HTML, takich jak:
•
•
•
•
PDF do drukowania
RSS do newsów
WML dla użytkowników telefonów
SOAP i XML-RPC dla implementacji Web Services
Wraz z rozwojem informatyki będą pojawiały się nowe technologie wyjściowe,
które nasza aplikacja powinna wspierać.
6.3. Sterownik
Sterownik jest sercem aplikacji wykonanej w technologii MVC. Sterownik
powinien być częścią świadomą żądania HTTP. Na podstawie analizy żądania
Sterownik powinien zdecydować, jakie akcje należy wykonać i jaki widok
wyświetlić. Sterownik jest również zasadniczą częścią każdej biblioteki
implementującej MVC. Widok i model muszą być dostosowane do specyfiki
Twojej aplikacji (czyli prawdopodobnie napisane przez Ciebie), ale sterownik w
zasadzie pozostaje bez zmian, więc nie ma potrzeby wyważać drzwi otwartych
przez kogoś innego.
Note
Sterownik w aplikacji jest tylko jeden, więc naturalnym rozwiązaniem w PHP jest jeden
skrypt, który używa parametrów zawartych w URLu lub w danych POST, aby wybrać
widok. Typowy adres będzie więc miał postać index.php?widok=ListaUżytkowników.
Alternatywnie, można używać mod_rewrite lub innych podobnych mechanizmów, aby
uzyskać np. URL postaci index.php/ListaUżytkowników.
Sterownik musi w jakiś sposób wiedzieć, co składa się na widok i model w Twojej
aplikacji, oraz co należy wykonać w odpowiedzi na żądanie HTTP. W tym celu
trzeba go jakoś skonfigurować. Jeżeli piszemy sterownik samemu, możemy
umieścić te mapowania bezpośrednio w kodzie, ale rozwiązanie to jest
nieelastyczne i oczywiście odpada w przypadku wykorzystania gotowego
sterownika. Na ogół będziemy więc mieli plik konfiguracyjny, w którym napisane
będzie, co należy wykonać w odpowiedzi na każde możliwe żądanie. Format tego
pliku zależny jest od implementacji.
Jeżeli aplikacja ma wyświetlić listę użytkowników, sprawa jest prosta: sterownik
tworzy odpowiedni widok, który z kolei pobiera z modelu odpowiednie dane i
prezentuje np. w postaci tabelki. Ale co zrobić, gdy mamy dodać nowego
użytkownika, a następnie wyświetlić listę? Widok, jak wiemy, nie może zmieniać
modelu. Model może oferować niezbędną funkcjonalność, ale sam z siebie jej nie
wywoła. Więc to sterownik musi obsłużyć dodawanie użytkowników, czyli
sprawdzić dane POST i wywołać odpowiednią metodę modelu.
http://www.php.pl
Strona 5 z 8
W tym celu należy do sterownika wprowadzić akcje. Akcja jest to pojedyncza
czynność wykonywana przez aplikację. Przykłady akcji to:
•
•
•
Dodaj użytkownika
Wyświetl artykuły
Dodaj produkt do koszyka
Taka akcja może być implementowana jako klasa tworzona przez kontroler,
wykonująca odpowiednie zadanie i przekazująca sterownikowi informację, jaki
widok należy wyświetlić. Jeżeli twoja aplikacja musi zmienić coś w modelu (np.
dodać użytkownika), właśnie akcja jest odpowiednim miejscem do tego.
Koncepcyjnie akcje są częścią sterownika. O ile główny "silnik" kontrolera może
być wielokrotnie używany, o tyle akcje są już specyficzne dla aplikacji.
7. Obsługa żądania
Generalnie, przepływ sterowania w aplikacji MVC przybiera dwie postacie:
•
•
użytkownik
żąda
wyświetlenia
określonego
widoku
(np.
lista
użytkowników)
użytkownik wysyła dane i oczekuje zmiany stanu aplikacji (np. dodania
użytkownika)
Pierwszy, prostszy przypadek ilustruje rysunek:
1. Żądanie użytkownika (czyli żądanie GET od przeglądarki) trafia do
kontrolera.
2. Sterownik określa, który widok jest odpowiedni dla tego żądania, tworzy
obiekt widoku i przekazuje mu sterowanie.
3. Widok tworzy potrzebne mu klasy modelu i prosi go o podanie
niezbędnych danych
4. Model wysyła zapytanie do bazy danych.
5. Baza danych zwraca modelowi odpowiednie dane.
6. Model zwraca dane widokowi.
7. Widok formatuje dane i wysyła użytkownikowi w postaci HTML.
http://www.php.pl
Strona 6 z 8
Drugi przypadek jest trudniejszy, ponieważ jest więcej pracy do wykonania:
1. Żądanie użytkownika (tym razem na ogół żądanie POST) trafia do
kontrolera.
2. Sterownik określa, że obsłużenie żądania wymaga wykonania akcji, tworzy
obiekt odpowiedniej akcji i przekazuje sterowanie.
3. Akcja sprawdza dane zawarte w POST, po czym tworzy potrzebne jej klasy
modelu i prosi o zmianę danych.
4. Model zmienia zawartość bazy danych.
5. Akcja informuje sterownik o zakończeniu przetwarzania i podaje, jaki
widok należy wyświetlić.
6. Sterownik tworzy odpowiedni obiekt widoku i przekazuje sterowanie.
7. Widok tworzy potrzebne mu klasy modelu i prosi go o podanie
niezbędnych danych
8. Model wysyła zapytanie do bazy danych.
9. Baza danych zwraca modelowi odpowiednie dane.
10.Model zwraca dane widokowi.
11.Widok formatuje dane i wysyła użytkownikowi w postaci HTML.
http://www.php.pl
Strona 7 z 8
8. Istniejące rozwiązania
Dla PHP istnieje wiele implementacji MVC. Znane mi projekty umieściłem poniżej.
nazwa
projektu
Ambivalence
Eocene
Mojavi
Phiend
php.MVC
Phrame
http://www.php.pl
krótki opis
Odpowiednik PHP projektu Maverick. Maverick jest przeznaczony
dla Javy i powstał jako próba uproszczenia Struts.
Framework rozwijany równolegle dla PHP i ASP.NET.
Zawiera
unikalne
rozwiązania,
np.
filtry
i
system
uwierzytelniania.
Projekt mojego autorstwa, zawiera obsługę uwierzytelniania,
logi i wyróżnia się szybkością.
Duży projekt, będący próbą przeniesienia Struts do PHP.
Wykorzystuje pakiet OOH Forms z biblioteki PHPlib.
Kolejny port Struts. Zawiera znane z Javy klasy kontenerowe
(np. ArrayList), opakowujące standardowe rozwiązania PHP.
Strona 8 z 8

Podobne dokumenty