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