MVC (model, widok, kontroler) to wzorzec organizowania kodu w aplikacji w celu poprawy możliwości konserwacji.
Wyobraź sobie fotografa z aparatem w studio. Klient prosi go o zrobienie zdjęcia pudełka.
Architektury inne niż MVC są ze sobą ściśle zintegrowane. Gdyby więc pudełko, kontroler i kamera były jednym i tym samym obiektem, musielibyśmy rozdzielić, a następnie przebudować zarówno pudełko, jak i kamerę za każdym razem, gdy chcieliśmy uzyskać nowy widok. Robienie zdjęcia zawsze byłoby jak próba zrobienia selfie - i to nie zawsze jest bardzo łatwe.
bwaha napisał:
Autor odwołuje się do mvctree.py w wxPython jako przykład projektu MVC. Jednak wciąż jestem zbyt zielony, więc uważam ten konkretny przykład za zbyt złożony i nie rozumiem podziału, który autor zaleca.
W MVC chodzi o rozdzielenie obaw.
Model odpowiada za zarządzanie danymi programu (zarówno danymi prywatnymi, jak i klientami). View / Controller odpowiada za zapewnienie światu zewnętrznemu środków do interakcji z danymi klienta programu.
Model zapewnia wewnętrzny interfejs (API) umożliwiający interakcję z innymi częściami programu. Widok / Kontroler zapewnia zewnętrzny interfejs (GUI / CLI / formularz internetowy / wysokopoziomowy IPC / itp.), Aby umożliwić wszystkim programom komunikację z nim.
Model jest odpowiedzialny za utrzymanie integralności danych programu, ponieważ jeśli zostanie to uszkodzone, to gra jest skończona dla wszystkich. Widok / Kontroler jest odpowiedzialny za utrzymanie integralności interfejsu użytkownika, upewnienie się, że wszystkie widoki tekstu wyświetlają aktualne wartości, wyłączenie elementów menu, które nie dotyczą bieżącego fokusa itp.
Model nie zawiera kodu widoku / kontrolera; bez klas widżetów GUI, bez kodu do układania okien dialogowych lub otrzymywania danych wejściowych od użytkownika. Widok / kontroler nie zawiera kodu modelu; brak kodu do sprawdzania poprawności adresów URL lub wykonywania zapytań SQL, a także brak stanu oryginalnego: wszelkie dane przechowywane przez widżety służą wyłącznie do wyświetlania, a jedynie odzwierciedlenie prawdziwych danych przechowywanych w Modelu.
Oto test prawdziwego projektu MVC: program powinien zasadniczo być w pełni funkcjonalny, nawet bez dołączonego View / Controller. OK, świat zewnętrzny będzie miał problem z interakcją z nim w tej formie, ale dopóki znamy odpowiednie inkantacje Model API, program będzie przechowywał i manipulował danymi w normalny sposób.
Dlaczego to jest możliwe? Cóż, prosta odpowiedź jest taka, że wszystko dzięki niskiemu sprzężeniu między warstwami Model i View / Controller. To jednak nie jest pełna historia. Kluczem do całego wzorca MVC jest kierunek, w którym podąża to połączenie: WSZYSTKIE instrukcje przepływają z widoku / kontrolera do modelu. Model NIGDY nie mówi View / Controller, co ma robić.
Dlaczego? Ponieważ w MVC, podczas gdy widok / kontroler może trochę wiedzieć o modelu (w szczególności API modelu), ale model nie może nic wiedzieć o widoku / kontrolerze.
Dlaczego? Ponieważ MVC polega na stworzeniu wyraźnego podziału obaw.
Dlaczego? Aby zapobiec złożoności programu wymykającej się spod kontroli i zakopaniu pod nim dewelopera. Im większy program, tym większa liczba komponentów w tym programie. Im więcej połączeń istnieje między tymi komponentami, tym trudniej jest programistom utrzymać / rozszerzyć / wymienić poszczególne komponenty, a nawet po prostu śledzić działanie całego systemu. Zadaj sobie to pytanie: czy patrząc na schemat struktury programu wolisz zobaczyć drzewo lub kołyskę kota? Wzorzec MVC unika tego drugiego, uniemożliwiając połączenia okrągłe: B może połączyć się z A, ale A nie może połączyć się z B. W tym przypadku A jest modelem, a B jest widokiem / kontrolerem.
BTW, jeśli jesteś bystry, zauważysz problem z właśnie opisanym ograniczeniem „jednokierunkowym”: w jaki sposób Model może informować Widok / Kontroler o zmianach w danych użytkownika Modelu, gdy Modelowi nie wolno nawet wiesz, że widok / kontroler, nie wspominając o wysyłaniu do niego wiadomości? Ale nie martw się: istnieje na to rozwiązanie i jest to całkiem fajne, nawet jeśli na początku wydaje się nieco ronda. Wrócimy do tego za chwilę.
W praktyce zatem obiekt View / Controller może, poprzez interfejs API Modelu, 1. powiedzieć Modelowi, aby zrobił rzeczy (wykonać polecenia) i 2. powiedzieć Modelowi, aby dał mu rzeczy (dane zwrotne). Warstwa Widok / Kontroler
wypycha instrukcje do warstwy Model i pobiera informacje z warstwy Model.
I właśnie tam twój pierwszy przykład MyCoolListControl poszedł nie tak, ponieważ API dla tej klasy wymaga, aby informacje zostały wepchnięte
do niej, więc wróciłeś do dwukierunkowego łączenia między warstwami, naruszając reguły MVC i wrzucając cię z powrotem do architektura kołyski kota, której [prawdopodobnie] starałeś się przede wszystkim uniknąć.
Zamiast tego klasa MyCoolListControl powinna iść z przepływem, wyciągając potrzebne dane z warstwy poniżej, gdy tego potrzebuje. W przypadku widżetu listy oznacza to na ogół pytanie o liczbę wartości, a następnie pytanie o każdy z tych elementów po kolei, ponieważ jest to najprostszy i najbardziej luźny sposób, aby to zrobić, a zatem ogranicza do minimum to, co istnieje. A jeśli widget chce, powiedzmy, przedstawić te wartości użytkownikowi w ładnej kolejności alfabetycznej, to jest to przesadzone; i oczywiście jego odpowiedzialność.
A teraz ostatnia zagadka, jak już wcześniej wspomniałem: jak utrzymać synchronizację ekranu interfejsu użytkownika ze stanem Modelu w systemie opartym na MVC?
Oto problem: wiele obiektów View ma charakter stanowy, np. Pole wyboru może być zaznaczone lub odznaczone, pole tekstowe może zawierać tekst edytowalny. Jednak MVC nakazuje, aby wszystkie dane użytkownika były przechowywane w warstwie modelu, więc wszelkie dane przechowywane przez inne warstwy do celów wyświetlania (stan pola wyboru, aktualny tekst pola tekstowego) muszą zatem być kopią pomocniczą tych podstawowych danych modelu. Ale jeśli stan modelu zmieni się, kopia tego stanu widoku nie będzie już dokładna i należy ją odświeżyć.
Ale jak? Wzorzec MVC uniemożliwia Modelowi wypchnięcie świeżej kopii tej informacji do warstwy Widok. Do licha, nawet nie pozwala modelowi wysłać widoku Zobacz, że jego stan się zmienił.
Cóż prawie. Okej, warstwa Modelu nie może rozmawiać bezpośrednio z innymi warstwami, ponieważ wymagałoby to znajomości tych warstw, a reguły MVC temu zapobiegają. Jeśli jednak drzewo spada w lesie i nikt go nie słyszy, czy wydaje to dźwięk?
Widzisz, odpowiedzią jest skonfigurowanie systemu powiadomień, zapewniającego warstwie modelu miejsce, w którym nikt nie będzie w stanie ogłosić, że właśnie zrobił coś interesującego. Inne warstwy mogą następnie wysyłać słuchaczy za pomocą tego systemu powiadomień, aby nasłuchiwać ogłoszeń, którymi są faktycznie zainteresowani. Warstwa Model nie musi wiedzieć nic o tym, kto słucha (a nawet jeśli ktoś w ogóle słucha!); po prostu publikuje ogłoszenie, a następnie zapomina o tym. A jeśli ktoś usłyszy to ogłoszenie i poczuje ochotę zrobić coś później - na przykład poprosić Modelkę o nowe dane, aby mógł zaktualizować wyświetlanie na ekranie - to świetnie. Model zawiera tylko listę powiadomień, które wysyła jako część definicji API; i to, co ktokolwiek robi z tą wiedzą, zależy od nich.
MVC zostało zachowane i wszyscy są zadowoleni. Struktura aplikacji może równie dobrze zapewniać wbudowany system powiadomień, lub możesz napisać własny, jeśli nie (patrz „wzorzec obserwatora”).
...
W każdym razie, mam nadzieję, że to pomaga. Kiedy zrozumiesz motywacje stojące za MVC, powody, dla których rzeczy są robione tak, jak są, zaczynają mieć sens, nawet gdy - na pierwszy rzut oka - wydają się bardziej złożone niż to konieczne.
Twoje zdrowie,
ma