Model-View-Controller (pol. Model-Widok-Kontroler) – wzorzec architektoniczny służący do organizowania struktury aplikacji posiadających graficzne interfejsy użytkownika[1]. Wiele prac traktuje go jako pojedynczy wzorzec, lecz może on być także traktowany jako złożony wzorzec wykorzystujący idee wzorców prostych, takich jak Obserwator, Strategia czy Kompozyt[1][2]. Oba te podejścia nie wykluczają się[1]. MVC nie był traktowany jako samodzielny wzorzec również w pracy „Design Patterns: Elements of Reusable Object-Oriented Software” autorstwa „Bandy Czworga”[2].
Model-View-Controller zakłada podział aplikacji na trzy główne części[3][4]:
- Model – jest pewną reprezentacją problemu bądź logiki aplikacji.
- Widok – opisuje, jak wyświetlić pewną część modelu w ramach interfejsu użytkownika. Może składać się z podwidoków odpowiedzialnych za mniejsze części interfejsu.
- Kontroler – przyjmuje dane wejściowe od użytkownika i reaguje na jego poczynania, zarządzając aktualizacje modelu oraz odświeżenie widoków.
Wszystkie trzy części są ze sobą wzajemnie połączone[1][3].
Model-View-Controller został zaprojektowany w 1979 roku przez norweskiego programistę Trygve Reenskaug pracującego wtedy nad językiem Smalltalk w laboratoriach Xerox[5] i początkowo nosił nazwę Model-View-Editor. Oryginalna implementacja została szczegółowo opisana we wpływowej pracy „Applications Programming in Smalltalk-80: How to use Model–View–Controller”[4].
Problem
Systemy komputerowe tworzone są do przetwarzania informacji. Zbiór informacji może mieć dowolną formę oraz dowolne znaczenie. Może to być jakiś wycinek rzeczywistości, np. proces przemysłowy czy obsługa operacji bankowych, jak i byt bardziej abstrakcyjny, np. zdjęcie przetwarzane przez program graficzny. Podstawowym problemem przy projektowaniu aplikacji jest zamodelowanie tego zbioru informacji w postaci cyfrowej jako algorytmy i struktury danych, a także znalezienie sposobu na zapewnienie użytkownikowi możliwości pracy z nimi, tj. zaprojektowanie interfejsu użytkownika. Interfejs użytkownika nigdy nie jest cyfrową reprezentacją, ale mechanizmem dostępu do niej. W typowej aplikacji te same dane często mogą być prezentowane na kilka różnych sposobów w zależności od kontekstu, lecz użytkownik cały czas musi mieć wrażenie, że operuje bezpośrednio na oryginalnych danych[5].
Budowa
W typowej aplikacji możemy wyróżnić zawsze trzy główne rodzaje aktywności: struktury danych, algorytmy przetwarzania oraz kanały komunikacyjne, które reprezentują określony problem lub dziedzinę rzeczywistości, komponenty do prezentacji danych oraz obsługę danych wejściowych z klawiatury i myszki, czyli reagowanie na poczynania użytkownika. W Model-View-Controller dla każdego z nich jest jawnie wydzielana osobna część, która komunikuje się z pozostałymi: model do reprezentowania danych przetwarzanych w programie, widok do generowania wyjścia i prezentowania wyników oraz kontroler do obsługi wejścia. Istotne jest, że każda z części MVC ma wyłączność na zarządzanie swoją częścią procesu – przykładowo nie może dojść do sytuacji, w której kod odpowiedzialny za wyświetlanie graficznego interfejsu użytkownika znajdzie się w modelu bądź kontrolerze.
Komunikacja między widokiem a kontrolerem jest łatwa do opisania, ponieważ oba te komponenty są stworzone do pracy ze sobą, jednak model musi komunikować się z nimi w sposób niejawny[4].
Model
Model jest aktywną reprezentacją pewnego fragmentu komputerowej reprezentacji problemu[5]. W danej aplikacji może istnieć kilka modeli tego samego elementu. Model jest samodzielny – do poprawnej pracy nie wymaga obecności dwóch pozostałych części, dlatego komunikacja z nimi musi zachodzić w sposób niejawny[4]. Wyróżniane są dwa podstawowe rodzaje modeli:
- pasywny model,
- aktywny model.
We współczesnych aplikacjach modele mają postać klas udostępniających pewien interfejs programistyczny do manipulacji strukturami danych oraz wykonywania pewnych akcji.
Pasywny model
Model pasywny reprezentuje elementy, które nigdy samoczynnie nie zmieniają swojego stanu. Żądanie zmiany stanu i wykonania jakichś operacji pochodzi zawsze z kontrolera oraz z widoku za pośrednictwem udostępnionego API, zatem nie ma potrzeby, by model musiał samodzielnie komunikować się z nimi. Przykładem może być model tekstu w edytorze. Wpisany tekst może ulec zmianie wyłącznie na żądanie kontrolera, który odebrał sygnał o naciśnięciu litery na klawiaturze. Kontroler sam powiadomi widok, że główne pole edytora musi być odświeżone. Nigdy nie dojdzie do sytuacji, w której tekst zmieni się samoczynnie i to model będzie musiał poinformować kontroler, że należy odświeżyć wyświetlany tekst.
Aktywny model
Aktywny model reprezentuje elementy, które mogą samoczynnie zmienić stan niezależnie od akcji wykonywanych przez użytkownika. Zmiana stanu najczęściej wymusza odświeżenie interfejsu użytkownika, dlatego aktywny model musi posiadać mechanizm, który umożliwi mu poinformowanie kontrolera o określonym wydarzeniu. Przykładem aktywnego modelu może być operacja kopiowania plików. Wraz z postępem kopiowania należy odświeżać pasek postępu, a dodatkowo aplikacja musi zostać powiadomiona o zakończeniu procesu kopiowania. W tym przypadku sygnały sterujące dla kontrolera muszą pochodzić z modelu.
W pracy „Applications Programming in Smalltalk-80: How to use Model–View–Controller”[4] prezentowana była implementacja aktywnego modelu, który komunikował się z widokami poprzez tzw. listy zależności, których idea działania przypominała współczesny wzorzec Obserwator[4]. Alternatywnym podejściem jest użycie Obserwatora do powiadamiania kontrolerów o zmianie stanu.
Widok
Widok jest odpowiedzialny za prezentację danych w obrębie graficznego interfejsu użytkownika. Może składać się z podwidoków zarządzających mniejszymi elementami składowymi. Widoki posiadają bezpośrednie referencje do modeli, z których pobierają dane, gdy otrzymują od kontrolera żądanie odświeżenia. Widoki mogą także modyfikować stan modelu, jeśli dana modyfikacja dotyczy sposobu prezentacji danych[5].
Kontroler
Zadaniem kontrolera jest odbiór, przetworzenie oraz analiza danych wejściowych od użytkownika. W typowej aplikacji źródłami danych wejściowych będą klawiatura i mysz. Po przetworzeniu odebranych danych kontroler może wykonać następujące czynności:
- zmienić stan modelu,
- odświeżyć widok,
- przełączyć sterowanie na inny kontroler.
Każdy kontroler posiada bezpośrednie wskazania na określone modele i widoki, z którymi współpracuje, a jednocześnie w aplikacji może istnieć wiele kontrolerów. W danym momencie tylko jeden z nich steruje aplikacją.
MVC jako wzorzec złożony
Autorzy publikacji poświęconych wzorcom projektowym zwracają uwagę, że wygodnie jest rozpatrywać MVC jako wzorzec złożony wykorzystujący prostsze wzorce[1][6]. Podstawowym sposobem rozbicia MVC na prostsze wzorce jest podział prezentowany przez[6]:
- Kompozyt w widoku – umożliwia tworzenie i pracę z zagnieżdżonymi widokami,
- Obserwator w modelu i widoku – umożliwia powiadamianie widoku przez model o zmianie stanu (aktywny model),
- Strategia w widoku i kontrolerze – widok pozostawia obsługę reakcji na zdarzenia wejściowe w gestii konkretnych implementacji kontrolera.
Gang Czworga rozszerza ten opis o dwa dodatkowe wzorce[2]:
- Metoda wytwórcza – pozwala wybrać domyślny kontroler dla całej aplikacji,
- Dekorator w warstwie widoku – pozwala dynamicznie rozszerzać widoki o nową funkcję.
Z kolei autorzy pracy[1] opisują MVC jako kompozycję siedmiu wzorców: Obserwator, Kompozyt, Most, Łańcuch zobowiązań, Metoda wytwórcza, Polecenie oraz niewchodzący w skład kanonu View Handler.
Konsekwencje użycia
Zalety:
- Brak zależności modelu od widoków – model jest niezależny od widoków, dlatego w aplikacji może współistnieć wiele widoków prezentujących te same dane na różne sposoby[7].
- Łatwiejsza rozbudowa widoków – interfejs użytkownika oraz warstwa prezentacji zmieniają się o wiele częściej niż logika biznesowa aplikacji. Ponieważ obie te części są oddzielone, można łatwo dodawać oraz modyfikować istniejące widoki bez wpływu na kluczową część systemu[7].
Wady[7]:
- Złożoność – implementacje MVC wprowadzają dodatkową warstwę abstrakcji oraz dodatkowe sposoby interakcji, czyniąc w ten sposób aplikację potencjalnie trudniejszą do debugowania[7].
- Kosztowne zmiany modelu – ponieważ model nie jest zależny od widoku, programiści rozwijający tę część nie muszą przejmować się zależnościami w przeciwnym kierunku. Jeżeli interfejs modelu ulega częstym zmianom, oznacza to konieczność poprawiania wszystkich korzystających z niego widoków[7].
- Trudne testowanie widoków – widoki są zależne od modeli, a ponadto zawierają własną, dodatkową logikę. Testowanie złożonych interfejsów użytkownika uważane jest za zadanie trudne[8].
Odmiany
W późniejszych latach na bazie wzorca MVC powstało wiele wzorców pochodnych o nieco innej strukturze oraz właściwościach, odmian oraz prób wykorzystania w nowych środowiskach[8]. Wzorzec był początkowo zdefiniowany wyłącznie dla aplikacji typu rich-client, natomiast od lat 90. rozwijany jest jego wariant dla aplikacji WWW.
Środowisko WWW
MVC zyskał dużą popularność wśród aplikacji WWW, jednak ze względu na specyfikę tego środowiska, oryginalne założenia wzorca musiały ulec pewnym modyfikacjom. Główną zmianą jest brak aktywnych modeli wynikający z zasady działania protokołu HTTP – odświeżanie wykonywane jest poprzez wysłanie przez użytkownika żądania HTTP, natomiast serwer nie ma możliwości wysłania odświeżonego widoku samoistnie, przez co powiadamianie o zmianie stanu modelu nie może zostać nigdy obsłużone.
Jednymi z pierwszych adaptacji MVC na potrzeby środowiska WWW były platformy Struts oraz Spring Framework w języku Java i technologii JavaServer Pages[9]. Zastosowano w nich dwa warianty MVC nazwane odpowiednio Model-1 oraz Model-2[10]. W pierwszym z nich dokument JSP pełni zarówno rolę widoku, jak i kontrolera. Brak jasnego rozdzielenia tych dwóch warstw prowadził jednak do dużego chaosu w kodzie[10]. W drugim z wariantów dokumenty JSP zostały zredukowane do pełnienia roli widoku. Serwlet pełni rolę kontrolera, który rozpakowuje żądanie HTTP, konfiguruje model i przekazuje go do widoku[10].
Własną implementację wzorca MVC posiada również firma Microsoft w postaci rozwiązania ASP.NET MVC[11]. Jest to platforma aplikacyjna zbudowana na bazie platformy ASP.NET, ale umożliwiająca programistom budowanie aplikacji z uwzględnieniem wyraźnie odseparowanych modeli, kontrolerów oraz widoków. Platforma ta posiada również dużą liczbę specyficznych rozszerzeń oraz konstrukcji programistycznych, a do jej głównych zalet należy duża elastyczność, pozwalająca na łatwe tworzenie np. własnych implementacji silników widoku.
Wzorce pochodne
W późniejszych latach na bazie wzorca MVC powstało wiele wzorców pochodnych o nieco innej strukturze oraz właściwościach:
- Model-View-Presenter – zmniejszenie znaczenia widoku na rzecz prezentera, który posiada pewną wiedzę o GUI oraz o tym, jak mapować poszczególne akcje użytkownika na zmiany modelu oraz zmiany widoku[12].
- Presentation-Abstraction-Control – odmiana MVP, w której rozpatrujemy hierarchiczne drzewo agentów Control zawiadujących przypisanymi im częściami Prezentacji i Abstrakcji, a także podrzędnymi agentami.
- Hierarchical Model-View-Controller – wzorzec analogiczny do PAC, lecz oparty na oryginalnym MVC.
- Pasywny widok – modyfikacja MVC oraz MVP zakładająca zerwanie powiązania między widokiem a modelem. Widok nie jest już odpowiedzialny za samodzielną aktualizację – logika jego działania przeniesiona jest do kontrolera[13].
- Model View Viewmodel (MVVM) – modyfikacja wzorca MVC, zawierająca specjalizację modelu prezentacji. Część ViewModel jest tutaj odpowiedzialna za udostępnianie danych z modelu do widoku w odpowiedni sposób. ViewModel zawiera często większą część logiki związanej z prezentacją danych[14].
Zastosowania
Idee wzorca MVC zdobyły szerokie uznanie wśród programistów, aczkolwiek w obecnych czasach dużo częściej implementowane są późniejsze odmiany zamiast oryginalnej definicji[8]. Model-View-Controller jest stosowany w bibliotekach GUI, np. Java Swing, zaś implementacje w środowisku WWW dla tego języka wykorzystują Model-2 (Spring Framework[10]). Framework Ruby on Rails przyczynił się do popularyzacji wśród aplikacji WWW pasywnego widoku, dalej określanego jednak przez autorów mianem MVC mimo znaczących różnic w budowie[15]. Na różnice w terminologii zwracają uwagę twórcy innego frameworka, Django[16].
Zobacz też
Przypisy
- 1 2 3 4 5 6 Frank Buschmann, Kevlin Henney, Douglas C. Schmidt: Pattern-oriented software architecture: On patterns and pattern languages Volume 5. Wiley, 2007, s. 178–179. ISBN 978-0471486480. (ang.).
- 1 2 3 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Design Patterns: Elements of Reusable Object-Oriented Software. 1995. ISBN 978-0-201-63361-0.
- 1 2 Martin Fowler: Patterns of enterprise application architecture. Addison-Wesley Professional, 2002, s. 330–331. ISBN 978-0321127426.
- 1 2 3 4 5 6 Steve Burbeck: Applications Programming in Smalltalk-80(TM): How to use Model-View-Controller (MVC). [dostęp 2010-10-14]. [zarchiwizowane z tego adresu (2012-04-29)]. (ang.).
- 1 2 3 4 Trygve Reenskaug: MVC, XEROX PARC 1978-79. [dostęp 2010-10-14]. (ang.).
- 1 2 3 Dirk Riehle: „Bureaucracy”: Pattern languages of program design 3. Addison-Wesley, 1997. ISBN 0-201-31011-2. (ang.).
- 1 2 3 4 5 Model-View-Controller. MSDN Library. [dostęp 2010-10-14]. (ang.).
- 1 2 3 Martin Fowler: GUI architectures. martinfowler.com. [dostęp 2010-10-15]. (ang.).
- ↑ Antonio Goncalves: Beginning Java EE 6 with GlassFish 3. Apress, 2010, s. 283. ISBN 978-1-4302-2889-9. (ang.).
- 1 2 3 4 Rob Harrop, Jan Machacek: Pro Spring. Apress, 2005, s. 600. ISBN 978-1590594612. (ang.).
- ↑ ASP.NET MVC Overview. asp.net. [dostęp 2013-04-06]. (ang.).
- ↑ Mike Potel: MVP: Model-View-Presenter – The Taligent Programming Model for C++ and Java. 1996. [dostęp 2010-10-15]. (ang.).
- ↑ Martin Fowler: Passive view. martinfowler.com. [dostęp 2010-10-15]. (ang.).
- ↑ Josh Smith: WPF Apps With The Model-View-ViewModel Design Pattern. MSDN Magazine February 2009. [dostęp 2011-02-10]. (ang.).
- ↑ Opis architektury w dokumentacji Ruby On Rails: Getting started: The MVC architecture – por. z innymi źródłami.
- ↑ Django FAQ: Django appears to be a MVC framework but you call the controller the „view” and the view the „template”. How come you don’t use the standard names?. [dostęp 2010-10-18]. (ang.).