Zasada jednolitej odpowiedzialności stwierdza, że „klasa powinna mieć jeden powód zmiany”.
We wzorze MVC zadaniem kontrolera jest mediacja między widokiem a modelem. Oferuje interfejs dla widoku do raportowania działań wykonanych przez użytkownika w GUI (np. Zezwalanie na widok do wywołania controller.specificButtonPressed()
) i jest w stanie wywoływać odpowiednie metody w modelu w celu manipulowania danymi lub wywoływania operacji (np. model.doSomething()
) .
To znaczy że:
- Kontroler musi wiedzieć o GUI, aby zaoferować View odpowiedni interfejs do zgłaszania działań użytkownika.
- Musi także wiedzieć o logice w Modelu, aby móc wywoływać odpowiednie metody w Modelu.
Oznacza to, że istnieją dwa powody do zmiany : zmiana GUI i zmiana logiki biznesowej.
Jeśli GUI ulegnie zmianie, np. Dodano nowy przycisk, Kontroler może potrzebować dodać nową metodę, aby umożliwić Widokowi zgłoszenie naciśnięcia tego przycisku przez użytkownika.
A jeśli logika biznesowa w Modelu ulegnie zmianie, Kontroler może ulec zmianie, aby wywołać prawidłowe metody w Modelu.
Dlatego Administrator ma dwa możliwe powody zmiany . Czy to łamie SRP?
źródło
Odpowiedzi:
Jeśli będziesz konsekwentnie rozumować na temat SRP, zauważysz, że „pojedyncza odpowiedzialność” jest w rzeczywistości gąbczastym terminem. Nasz ludzki mózg jest w stanie w jakiś sposób odróżnić różne obowiązki, a wiele obowiązków można przekształcić w jedną „ogólną” odpowiedzialność. Wyobraź sobie na przykład, że we wspólnej czteroosobowej rodzinie jest jeden członek rodziny odpowiedzialny za zrobienie śniadania. Teraz, aby to zrobić, trzeba ugotować jajka i tostów chlebowych i oczywiście ustawić zdrową filiżankę zielonej herbaty (tak, zielona herbata jest najlepsza). W ten sposób możesz rozbić „robienie śniadania” na mniejsze części, które razem są abstrakcyjne do „robienia śniadania”. Zauważ, że każdy utwór jest również obowiązkiem, który można np. Przekazać innej osobie.
Powrót do MVC: jeśli mediacja między modelem a widokiem nie jest jedną odpowiedzialnością, lecz dwiema, to jaka byłaby kolejna warstwa abstrakcji powyżej, łącząca te dwie? Jeśli nie możesz go znaleźć, albo nie wyodrębniłeś go poprawnie, albo nie ma żadnego, co oznacza, że wszystko w porządku. I wydaje mi się, że tak jest w przypadku kontrolera, obsługującego widok i model.
źródło
Jeśli klasa ma „dwa możliwe powody zmiany”, to tak, narusza SRP.
Kontroler powinien zwykle być lekki i ponosić wyłączną odpowiedzialność za manipulowanie domeną / modelem w odpowiedzi na pewne zdarzenie sterowane przez GUI. Możemy uznać każdą z tych manipulacji za zasadniczo przypadki użycia lub funkcje.
Jeśli do GUI zostanie dodany nowy przycisk, kontroler powinien zmienić tylko wtedy, gdy ten nowy przycisk reprezentuje jakąś nową funkcję (tj. W przeciwieństwie do tego samego przycisku, który istniał na ekranie 1, ale jeszcze nie istniał na ekranie 2, i tak jest dodano do ekranu 2). Konieczna byłaby również odpowiednia nowa zmiana w modelu, aby obsługiwać tę nową funkcjonalność / funkcję. Kontroler nadal ma jedynie obowiązek manipulowania domeną / modelem w odpowiedzi na pewne zdarzenie sterowane przez GUI.
Jeśli logika biznesowa w modelu zmienia się z powodu naprawienia błędu i wymaga zmiany kontrolera, jest to szczególny przypadek (lub być może model narusza zasadę otwartego zamknięcia). Jeśli logika biznesowa w modelu zmieni się, aby obsługiwać nowe funkcje / funkcje, niekoniecznie ma to wpływ na kontroler - tylko wtedy, gdy kontroler musi ujawnić tę funkcję (co prawie zawsze miałoby miejsce, w przeciwnym razie dlaczego miałoby być dodane do model domeny, jeśli nie będzie używany). Tak więc w tym przypadku kontroler musi zostać zmodyfikowany, aby obsługiwał model domeny w nowy sposób w odpowiedzi na pewne zdarzenie sterowane przez GUI.
Jeśli kontroler musi się zmienić, ponieważ powiedzmy, że warstwa trwałości zostaje zmieniona z płaskiego pliku na bazę danych, to z pewnością narusza SRP. Jeśli kontroler zawsze działa na tej samej warstwie abstrakcji, może to pomóc w osiągnięciu SRP.
źródło
Kontroler nie narusza SRP. Jak sam twierdzisz, jego zadaniem jest mediacja między modelami a widokiem.
To powiedziawszy, problem w twoim przykładzie polega na tym, że łączysz metody kontrolera z logiką w widoku, tj
controller.specificButtonPressed
. Nazwanie metod w ten sposób wiąże kontroler z GUI, nie udało się poprawnie wyodrębnić rzeczy. Administrator powinien polegać na wykonywaniu określonych czynności, tj .controller.saveData
Lubcontroller.retrieveEntry
. Dodanie nowego przycisku w GUI niekoniecznie oznacza dodanie nowej metody do kontrolera.Naciśnięcie przycisku w widoku oznacza zrobienie czegoś, ale cokolwiek to jest, można łatwo uruchomić w dowolny inny sposób, a nawet nie poprzez widok.
Z artykułu w Wikipedii o SRP
Kontroler nie przejmuje się tym, co jest w widoku, ale gdy wywoływana jest jedna z jego metod, zapewnia on określone dane do widoku. Musi tylko wiedzieć o funkcjonalności modelu, o ile wie, że musi wywoływać metody, które będzie miał. Nic więcej nie wie.
Wiedza, że obiekt ma metodę wywoływania, nie jest tym samym, co znajomość jego funkcjonalności.
źródło
specificButtonsPressed()
jest to, że przeczytałem, że widok nie powinien wiedzieć nic o funkcjonalności jego przycisków i innych elementów GUI. Nauczono mnie, że po naciśnięciu przycisku widok powinien po prostu zgłosić się do kontrolera, a kontroler powinien zdecydować „co to znaczy” (a następnie wywołać odpowiednie metody w modelu). Wywołanie widokucontroller.saveData()
oznacza, że widok musi wiedzieć, co oznacza naciśnięcie tego przycisku, oprócz faktu, że został naciśnięty.specificButtonPressed()
), rzeczywiście kontroler nie byłby tak związany z GUI. Czy powinienem porzucićspecificButtonPressed()
metody? Czy myślę, że przewaga tych metod ma dla ciebie sens? A może posiadaniebuttonPressed()
metod w kontrolerze nie jest warte kłopotów?specificButtonPressed()
metody w kontrolerze, jest to, że oddziela widok z rozumieniu go za naciśnie przycisk całkowicie . Wadą jest jednak to, że w pewnym sensie wiąże kontroler z GUI. Które podejście jest lepsze?foo
lub równie łatwofireZeMissiles
. Będzie wiedział tylko, że powinien zgłosić się do określonej funkcji. Nie wie, co robi funkcja, tylko to ją wywoła. Kontroler nie przejmuje się tym, w jaki sposób wywoływane są jego metody, tylko że będzie reagować w określony sposób, kiedy będą.Pojedyncza odpowiedzialność kontrolerów ma być umową pośredniczącą między widokiem a modelem. Widok powinien być odpowiedzialny tylko za wyświetlanie, model powinien być odpowiedzialny tylko za logikę biznesową. Odpowiedzialnością kontrolerów jest wypełnienie tych dwóch obowiązków.
To wszystko dobrze i dobrze, ale nieco wyruszę poza środowisko akademickie; Kontroler w MVC składa się zasadniczo z wielu mniejszych metod działania. Te działania zasadniczo odpowiadają rzeczom, które można zrobić. Jeśli sprzedaję produkty, prawdopodobnie będę mieć ProductController. Ten kontroler będzie miał akcje takie jak GetReviews, ShowSpecs, AddToCart itp.
Widok ma SRP wyświetlania interfejsu użytkownika, a część tego interfejsu zawiera przycisk z napisem AddToCart.
Administrator ma SRP znajomości wszystkich widoków i modeli zaangażowanych w proces.
AddToCart Action kontrolerów ma określoną zasadę SRP polegającą na znajomości wszystkich osób, które muszą być zaangażowane, gdy element zostanie dodany do koszyka.
Model produktu ma SRP modelowania logiki produktu, a ShoppingCart Model ma SRP modelowania, w jaki sposób elementy są zapisywane do późniejszego finalizowania. Model użytkownika ma SRP modelowania użytkownika, który dodaje rzeczy do koszyka.
Możesz i powinieneś ponownie wykorzystywać modele, aby załatwić swoją działalność, a modele te muszą zostać połączone w pewnym momencie w kodzie. Sterownik kontroluje każdy unikalny sposób zachodzenia sprzężenia.
źródło
Na kontrolerach spoczywa tylko jedna odpowiedzialność: zmiana stanu aplikacji na podstawie danych wprowadzonych przez użytkownika.
source: wikipedia
Jeśli zamiast tego masz „kontrolery” w stylu Railsów (które żonglują aktywnymi instancjami rekordów i głupimi szablonami) , to oczywiście łamie SRP.
Z drugiej strony aplikacje w stylu Railsów nie są tak naprawdę MVC.
źródło