Czy w MVC dobrą praktyką jest posiadanie prywatnych, niedziałających funkcji w klasie kontrolera?

10

Czasami funkcje akcji w klasie kontrolera mogą stać się ogromne i nieprzyjemne, z wieloma wierszami kodu, aby po prostu kontrolować przepływ danych z modelu do widoku. W pewnym momencie te ogromne funkcje całkowicie tracą orientację w podstawowych zasadach dobrego kodu, tj. Robią tylko jedną rzecz, są małe, czytelne i łatwe do zarządzania itp.

Czy dobrą praktyką byłoby podzielenie tych ogromnych funkcji akcji na mniejsze funkcje prywatne w klasie kontrolerów, czy też potrzeba takiej optymalizacji oznacza, że ​​powinniśmy raczej dodać je do modelu?

Głosowałbym za tym, by mniejsze funkcje były prywatne w kontrolerze, tak aby były względne w stosunku do akcji, ale słyszałem argumenty, że kontroler powinien być raczej prosty, podczas gdy model może stać się ogromny i zbity; i zastanawiałem się, która z nich byłaby najbardziej preferowaną metodą.

David „łysy imbir”
źródło

Odpowiedzi:

16

To może nie być najlepsza analogia, ale pomyśl o kontrolerze tak samo, jak myślisz o sieci pająka. Jego jedynym zadaniem jest łapanie much (żądań), aby pająk (leżące poniżej warstwy) mógł strawić. Sieć może łapać i trzymać mniejsze lub większe muchy (modele). Rolą pająka nie jest trawienie ofiary, choć można jej w tym celu wykorzystać. Im cieńsza i czystsza sieć, tym łatwiej zarabiać na pająka.

Możesz zastosować tę samą logikę do swojej aplikacji MVC. Ogromne i nieprzyjemne funkcje, które opisujesz, są najprawdopodobniej zachowaniem modelu i powinny należeć do modelu (zwróć uwagę, że model to nie tylko obiekt wyświetlany w widoku). Jeśli zachowanie modelu ulegnie zmianie, należy zmienić model, a nie kontroler, który go obsługuje.

Ponadto zachowanie ich jako prywatnych metod w kontrolerze tylko zaśmieci je i utrudni utrzymanie. To także ustępuje złemu nawykowi, ponieważ inne osoby zaangażowane w rozwój byłyby kuszone, aby zrobić to samo, ponieważ widziały to już wcześniej w projekcie.

diabelnie
źródło
+1 za twórczą analogię. :) Robisz ciekawy punkt. Zwłaszcza na kształtowanie złego nawyku. Dziękuję Ci.
David „łysy imbir”
8

Najlepsza odpowiedź, jaką mogę udzielić, to zacytowanie wspaniałej książki Roberta Martina „Clean Code”, którą gorąco polecam wszystkim zainteresowanym tym tematem:

Pierwszą zasadą funkcji jest to, że powinny być małe. Druga zasada mówi, że powinny być mniejsze.

Nie mogę tego lepiej powiedzieć. Obowiązuje kolejny świetny cytat z tej samej książki:

Funkcje powinny zrobić jedną rzecz. Powinny to zrobić dobrze. Powinni to zrobić tylko.

Dzieląc kod na więcej funkcji, musisz nadać tym funkcjom sensowne nazwy, które mogą znacznie poprawić czytelność kodu. Nie trzeba dodawać, że wszystkie funkcje nieprzeznaczone do użycia poza klasą powinny być prywatne, aby można było łatwo ponownie użyć kodu poprzez dziedziczenie.

Jeśli kontroler ma teraz zbyt wiele funkcji, jest to znak, że prawdopodobnie robi zbyt wiele. Następnie możesz podzielić go na kilka niezależnych elementów lub spróbować przenieść niektóre funkcje do modeli, jak wspomniano w drugiej odpowiedzi. Również jeśli podążasz za nieklasycznym smakiem MVC, w którym Widoki mogą mieć pewną logikę, możesz umieścić tam niektóre swoje funkcje, kiedy tylko to pasuje.

Dmitri Zaitsev
źródło
1
Nie sądzę, że umieszczanie logiki biznesowej w widokach to „nieklasyczne MVC”, to po prostu „złe MVC”. Oczywiście potrzebujesz podstawowych struktur kontroli w widokach, ale powinny one być dostosowane do obaw użytkownika / interfejsu użytkownika, a nie domen / biznesu. Rzeczywista funkcja w widoku jest dość okropna.
Aaronaught
1
@Aaronaught Byłem niejasny z „jakąś logiką”, co miałem na myśli to np. Biblioteka Backbone.js, w której umieszczasz zdarzenia i funkcje użytkownika, aby sobie z nimi poradzić. W klasycznym MVC jest to zadanie kontrolera. Może to jednak być niepraktyczne, ponieważ za każdym razem, gdy zmienia się interfejs użytkownika, należy dostosować zarówno widok, jak i kontroler. Umieszczając funkcje obsługi interfejsu użytkownika w widoku, wystarczy dostosować widok. To tylko mój subiektywny pogląd - czy coś mi brakuje?
Dmitri Zaitsev,
1
To, że coś jest dostarczane po stronie klienta, nie oznacza, że ​​jest to logicznie część widoku. Powiązania danych w widokach, jasne, ale Backbone sam w sobie jest strukturą MV * (rodzaj MVC, rodzaj MVP, też niezupełnie), a skrypty po stronie klienta powinny być odpowiednio zorganizowane; w przeciwnym razie hakujesz.
Aaronaught
0

W MVC staram się, aby mój kontroler był tak „cienki”, jak to możliwe, a moje modele były tak głupie, jak to możliwe.

Potrzebne funkcje logiczne i pomocnicze zostają umieszczone w osobnych, niezależnych klasach pomocniczych. To sprawia, że ​​moje testowanie jest również znacznie łatwiejsze (testujesz ... prawda?: D) Testowanie kontrolerów jest niezwykle trudne, za każdym razem, gdy próbujesz utworzyć instancję kontrolera do przetestowania, musisz pomyśleć o kontekście HTTP i fałszowaniu http to i tamto, i to jest ból, ale celowo jest to ból. Potrzebujesz tych wszystkich rzeczy, ponieważ kontroler jest tak ściśle powiązany z HTTP i Internetem. Jest to punkt wejścia do Twojej aplikacji internetowej.

Funkcje logiczne i pomocnicze nie mają nic wspólnego z siecią. Są całkowicie agnostyczne dla środowiska (a przynajmniej powinny). To samo powinno ci powiedzieć, że nie należą do siebie w tym samym miejscu. Ponadto, jeśli wszystkie logiki aplikacji są ściśle powiązane z Internetem lub konkretną implementacją sieci, nigdy nie można zabrać go ze sobą.

Opracowaliśmy naszą witrynę MVC ze wszystkimi naszymi jednostkami bazy danych (nie naszymi modelami mvc, naszymi rzeczywistymi jednostkami db), naszą pamięcią, naszymi klasami pomocniczymi i naszą logiką w oddzielnych niezależnych bibliotekach DLL. Każdy z nas miał tylko jedną stronę internetową, ale i tak to zrobiliśmy.

Kilka miesięcy temu zostaliśmy poproszeni o utworzenie kilku aplikacji komputerowych, które są powiązane z kilkoma naszymi systemami dodatkowymi. Zrobiono to łatwo, ponieważ cały nasz przetestowany kod można łatwo ponownie wykorzystać. Gdybyśmy umieścili nasz kod w naszym projekcie internetowym lub w naszych kontrolerach, nigdy nie bylibyśmy w stanie tego zrobić.

astronauta
źródło
2
Model w MVC jest jedyną warstwą, która nie powinna być głupia. Jeśli smartów nie ma w modelu i nie ma ich w kontrolerze, to gdzie oni są ... w widoku? Kontrolery również nie powinny być trudne do przetestowania; możliwość użycia DI i podróbek / próbnych w celu ułatwienia testów jednostkowych jest jedną z zalet MVC w porównaniu z innymi platformami. Większość moich testów kontrolera jest poniżej 5 linii.
Aaronaught
użyłbym klasy „pomocnika” z logiką zamiast przenikać model z logiką. jaką logikę umieściłbyś w modelu? czy wie, jak się ładować i ratować? Zgadzam się, że fałszowanie / karczowanie jest łatwe, ale nie jest to wymówka, aby zacząć tuczić swoje kontrolery.
kosmita
Mam wrażenie, że ta odpowiedź oznacza dobrze, ale jest niepoprawnie sformułowana… lub może różni się terminologią.
Simon Whitehead,
3
Klasy „pomocników” nie są elementem architektonicznym. Są albo częścią M, V lub C. Jeśli nie jesteś pewien, który z nich, to ci pomocnicy nie mają spójności . Słowo „pomocnik” również zalicza się tutaj do „uchwyt”, „do”, „perform” i przerażającego menedżera .
Aaronaught
@ SimonWhitehead: Większość odpowiedzi znaczy dobrze, ale wiele z nich jest niepoprawnych. To niestety promuje nieporozumienie ze względu na znaczenie „Modelu” lub zaleca umieszczenie poza nim krytycznej logiki biznesowej. Miałem wątpliwą przyjemność z utrzymywania stron MVC z zillionem „pomocników” - są okropne.
Aaronaught
-2

Poza Dmitrijem Zaitsevem i świetnymi odpowiedziami kosmonautów nie wiem, czy poniższe informacje dotyczą również PHP: Powinieneś próbować unikać prywatnych metod z powodu braku możliwości automatycznego testowania.

Tak, możesz użyć metaprogramowania lub wstrzykiwania zależności również do testowania metod prywatnych, ale nie powinieneś tego robić, ponieważ ma to ogromny wpływ na czytelność twojego kodu.

Zawsze pamiętaj o zasadzie KISS: Prosto, głupio.

cHaOs667
źródło
5
To nie jest dobry powód do unikania prywatnych metod, a także nie ma nic wspólnego z architekturą MVC. Nie próbujesz testować metod prywatnych, powinny one być objęte testami metod publicznych . Jeśli nie możesz ich opisać, oznacza to, że twoja klasa jest zbyt złożona i wymaga ponownej analizy; nie oznacza to, że nie powinieneś mieć prywatnych metod lub (mam szczerą nadzieję, że nie o to tak naprawdę chodziło), że powinny one być publiczne.
Aaronaught