MVC jest królem! Z modelu, przez kontroler, do widoku! Mówili. Słuchaliśmy. Polegliśmy.
Niezależnie od wykorzystywanej technologii czy frameworka: jeżeli “dziubiesz webówki” to prawdopodobnie działasz w MVC. I to “C” może reprezentować różne pojęcia. Czasami jest to Controller, czasami Module, czasami coś jeszcze innego.
Reality check
Ale co to jest ten kontroler? Jak sama nazwa wskazuje: coś, co kontroluje. Czyli: musi być ważne i poważne, prawda? Pan kontroler biletów w autobusie to nawet mandat może wypisać, a w zły dzień to i “wryjdaćmożedać“. Ale: skoro jest to coś ważnego, to wypada o to dbać, tak? Oczywiście! – zakrzykniesz ochoczo.
O kontrolery trzeba dbać? Trzeba? To teraz zerknij, misiu, w swój kod…
To teraz zerknij w trzewia dowolnego kontrolera w swoim systemie. Albo dowolnego projektu open source. I co widzisz? Ano… kod, oczywiście. Niezmierzone morze kodu. Ile razy musisz wcisnąć page down, żeby dojechać do końca pliku? 5, 10 czy może 100? Normalka.
Coś gdzieś poszło nie tak. Po necie krąży sporo memów, heheszków z rozdętych klas “Utils”, “Common”, “XManager” itd. To teraz zastanówmy się: czym od takich śmiechowych klas różni się typowy kontroler? Ręki może nie, ale paznokieć dam sobie uciąć, że w ogromnej większości przypadków: niczym. Brudny, poplamiony, połatany, rozdęty, poprzecierany wór spięty sznurem od snopowiązałki.
Diagnoza
Na konferencjach i szkoleniach opowiadam o SOLID, dużą wagę przywiązując do Single Responsibility Principle oraz Dependency Inversion Principle. I to jest ciekawa sprawa, bo… obie te zasady pięknie pokazują, czym zwykle jest kontroler.
Więcej o SRP i DI dowiesz się tutaj: cykl postów, prezentacja, screencasty.
Zacznijmy od DI. Standardowa implementacja Dependency Injection polega na tym, że wszystkie komponenty, z których klasa korzysta, są przekazane do niej jako parametry konstruktora. Jasne? Oczywiście. Zdrowy rozsądek i dobre praktyki mówią, że jeżeli liczba takich parametrów niebezpiecznie rośnie, to “coś jest nie tak”. Ta “zbyt duża liczba parametrów” jest definiowana różnie. Dla jednego będzie to 5, dla innego 7. Dla mnie to 3. Ponownie otwórz kontroler w swoim projekcie i policz parametry jego konstruktora. Moja 3-letnia córka jeszcze pół roku temu umiała liczyć tylko do 12, więc niestety w większości przypadków pewnie by poległa. Gdybyśmy mieli zdigitalizowany odpowiednik Domestosa, to wylałbym go w całości na kontrolery właśnie. To TU znajdziesz siedlisko wszelkiego ohydztwa. To TU masz klasy z 15-20 parametrami, z których większość wykorzystana jest tylko w jednej metodzie. To TU znajdziesz dziesiątki niepowiązanych ze sobą metod, zgrupowanych w jednym worku, bo “nazwa jest podobna”. Bo “URLe muszą mieć taki sam prefix”.
Zwykle rolą kontrolera jest sprawienie, żeby działał routing.
DI pokazuje, że jest problem. Ale jaki problem?
Problem z SRP. Kto z Was widział kiedyś kontroler, który robi jedną rzecz? Ma jedną odpowiedzialność? I ta odpowiedzialność nie może być określona np. jako “obsługuje księgarnię internetową”. Albo “realizuje operacje bankowe”. Na tym stopniu granularności każdy system można by wrzucić w jeden kontroler i pozamiatane.
Więc właśnie – kontrolery zawierają całą masę akcji. I to każda AKCJA realizuje jedną odpowiedzialność. Zebranie ich wszystkich do jednej brzydkiej kupy, zwanej kontrolerem, to po prostu praktyka. Bo tak napisane w dokumentacji czy tutorialu. Bo tak uczyli na studiach. Wreszcie: bo tak już było w projekcie, do którego dołączam.
Ostatnimi czasy do bloga dołączyły tysiące nowych Czytelników, więc Wam wszystkim przypomnę świętą zasadę, którą doskonale znają starzy bywalcy: “Think for yourself. Question authority.“. Co jeżeli wykładowcy na studiach się mylą? Albo nie tyle mylą, co powtarzają zdania przeczytane w dokumentacji. Dokumentacji, która prawdopodobnie bazuje na jakimś banalnym przykładzie, zamykającym się w 2-3 operacjach. Takie podejście się nie skaluje. Czego zresztą jesteś świadkiem, zerkając w kod, w którym grzebiesz na co dzień.
Leczenie
Skoro kontroler to tylko tępy wór na akcje – po co nam on? Przecież głównym jego zadaniem jest przyjęcie miliona zależności na potrzeby pojedynczych akcji.
Skoro kontroler to tylko klasa wymagana przez framework, aby odpowiednio zdefiniować routing – dlaczego godzimy się na to, zamiast pokazać maszynom, kto tu rządzi? Przecież nie trzeba bazować na standardowej konfiguracji dowolnego frameworka przy generowaniu URLi (<controller_name>/<action>/<params>).
Kontroler jest jak wyrostek. Jest zbędny, do wycięcia. Powinniśmy skupiać się na akcjach.
Wreszcie: skoro akcja jest naszą jednostką, w ramach której tak naprawdę realizujemy jedną operację i jesteśmy w stanie zaimplementować wszystkie dobre praktyki, to… chyba właśnie na akcjach, a nie kontrolerach, powinniśmy się skupić, co nie?
Kontrolery owszem, złowrogo kontrolują. I ograniczają. Nas, programistów. Ubijmy je.
To akcja powinna być klasą. Grupowanie ich w sztuczne byty zwane kontrolerami nie sprawdza się. Ja to wiem, i Ty też to wiesz.
Co zatem można zrobić?
MVA: Model/View/Action
Po pierwsze: z każdego kontrolera wyodrębnić osobą klasę dla każdej akcji. Prawdopodobnie będzie to oznaczało utworzenie X “mini-kontrolerów”, żeby używany framework potrafił całość spiąć do kupy, ale… to nie ma znaczenia. Te małe klasy niech dziedziczą z klasy “ControllerBase”, czy jakkolwiek by się to nie nazywało. To jest tylko po to, aby uszczęśliwić maszynę. Maszyna zobaczy “Controller”, ucieszy się, i będzie działać. Ale my jesteśmy mądrzejsi od maszyn. Dla nas będą to akcje.
Po drugie: skonfigurować routing tak jak chcemy. Znaleźć sposób na wyrzucenie konwencji sterujących procesem generowania URLi gdzieś na zewnątrz, do osobnych bytów. Kto to widział, żeby struktura adresów wpisywanych w przeglądarce definiowała strukturę klas w systemie! Ada Lovelace zabiłaby nas śmiechem.
Po trzecie: ucieszyć się, widząc jak automatycznie “samo” spada skomplikowanie kodu, liczba zależności w konstruktorach, licznik linii kodu per plik (wbrew pozorom: to JEST istotne). I klaskać jak dziecko z lizakiem, gdy rodzi się możliwość ogarnięcia powstałych klas normalnym ludzkim mózgiem.
Horyzont
Taki proces niesie za sobą kolejną zaletę: otwiera drogę do totalnego “ogłupienia” warstwy kontrolerów. Ta warstwa aplikacji, zależna od frameworków i wiążąca nas z cudzym kodem, powinna być ultra-cienka. Mega-głupia. Uber-prosta. Tak, aby kompletnie odseparować NASZ kod od kodu powstałego na potrzeby zewnętrznych bibliotek. Zawierając tam jakąkolwiek logikę biznesową: strzelamy sobie w kolano. Z łuku.
Warstwa aplikacji, wiążąca nas z kodem frameworków, powinna być ultra-cienka
Ale to innym razem.
Ale cliffhanger, co nie? Z tego klifu dyndając, wykorzystaj drugą kończynę górną i… dopisz się do newslettera poniżej! Chyba, że to ja z klifu dyndam, patrząc z przerażeniem na rozwierające się w dole kontrolerowe, tfu, krokodyle, paszcze.
P.S. Wiem, że co do zbędności wyrostka są różne teorie i średnio mnie to interesuje ;).
Fajny wpis, miło poczytać wkońcu o takich rzeczach po polsku :). Kontrolery mają jeszcze jedną poważną wadę, ich proces konstrukcji jest zwykle ukryty. Powoduje to, że zaczynamy walczyć z frameworkiem i tworzyć łaty w obrębie jego metod, które przetworzą nam ten pseudo MVC w coś co chcemy napisać. Warto też wspomnieć, że zwykle kontroler we frameworku webowym ma niewiele wspólnego z literą C z MVC. Główna idea MVC polega na powiadamianiu widoku wprost z modelu. W webie wszystko routujemy przez kontroler. Trafiłem swego czasu na ciekawy framework, który właśnie próbuje przerwać zmowę milczenia i nie zobaczymy w nim wogóle warstwy “C” (http://www.takes.org/). Swoją drogą masz gdzieś przykład kodu z takimi mini kontrolerami?
Grzegorz,
Ukryty proces konstrukcji… z jednej strony tak, ale z drugiej: chyba każdy sensowny framework pozwala napisać własną implementację “controller factory”, czy jak by się to nie nazywało, i dość prosto wpiąć w cały mechanizm, integrując np ulubiony kontener DI.
Co do kodu – nie, przykładu open source nigdzie nie mam.
Thx za link :).
Proszę bardzo – framework, w którym jedna akcja = jedna klasa: http://hanamirb.org/guides/actions/overview/
Aż miło sie na sercu robi jak człowiek przeczyta mądre rady i zauważy, że to robi u siebie w projekcie a doszedł do tego sam :)
Dodam, że przecież w samym kontrolerze możemy zdefiniować jaki routing/url ma być użyty aby trafić do tej konkretnej akcji.
np.
[Route(“{productId:int}/{productTitle}”)]
public ActionResult Show(int productId) { … }
Piotr,
W róznych frameworkach, różnych technologiach, jest to robione… różnie :). Dlatego nie dawałem przykładów kodu, bo treść kierowana do wszystkich, a nie tylko MVC w .NET.
Dawno temu obejrzałem prezentację Sandro Mancuso na temat organizacji kodu w projekcie i obowiązkach poszczególnych warstw.
Autor przekonywał, że kontrolery powinny być naiwne i nawigować do klas implementujących przypadki użycia. Osobom, którym temat wydaje się ciekawy polecam przejrzeć sobie ten wykład. Slajdy dostępne są tutaj: http://www.slideshare.net/sandromancuso/crafted-design-geecon-2014#22, natomiast nagranie: https://vimeo.com/101106002
Paweł,
Oczywiście że tak jest, kontroler i jego akcje: to powinno być kilka linijek kodu.
Problem z ciągnącymi się kilometrami kontrolerami nie wynika z nadmiaru akcji, lecz z tego, że ludzie nie rozumieją na czym MVC polega i w kontrolery wpychają wszelką logikę: aplikacyjną, walidacyjną, biznesową, infrastrukturalną i rozmytą. Tymczasem kontroler powinien tylko odebrać żądanie, wywołać serwis i w zależności od jego wyniku, przekierować na odpowiedni widok. Gdyby ludzie się tego trzymali, to nawet 20 akcji w kontrolerze nie byłoby wielką tragedią, bo i tak zajęłyby mniej niż 100 linijek.
Zgodzę się, że Twoje podejście to ciekawa idea, ale kontroler per akcja brzmi z kolei jako przesada w drugą stronę. Oczywiście implementacja Twojego pomysłu skróciłaby kontrolery… ale one nadal nie byłyby kontrolerami, tak jak nie są nimi teraz.
Kontrolery w MOIM projekcie wyglądają tak: https://github.com/dwdkls/pizzamvc/blob/master/sample/KebabManager.Web/Controllers/OrdersController.cs Przy czym większość tego kodu właściwie powinna być gdzie indziej, bo definiuje ona tak naprawdę jak ma strona wyglądać. (Ale to jest akurat moja wina – zły pomysł, na który wpadłem dawno temu i widzę potrzebę poprawy, tylko czasu nie mam.)
Ale to jest właśnie MÓJ projekt, więc trzymam się MVC i nie mam logiki w kontrolerach. W projektach w PRACY jest inaczej – dokładnie tak jak, opisałeś w swoim poście. Tylko ja się tego nie wstydzę, bo nie mam na to wpływu. Próba nauczenia architekta z piętnastoletnim stażem w branży (czy też “senior uber principal ASP.NET MVC developera” z trzyletnim, bo tyle na seniora przecież wystarczy) czym tak naprawdę jest MVC, to jak uczenie psa korzystania z kuwety. Produktywniej ten czas można spędzić czytając blogi ludzi rozumnych, ucząc się nowego frameworka dla siebie, albo nawet obserwując farbę schnącą na ścianie.
Dawid,
Powoli, oczywiście że docelowo chcielibyśmy mieć piękny system, w którym kontrolery robią to co powinny robić kontrolery, a nie WSZYSTKO. Ale po kolei.
Kontroler ma za zadanie odebrać parametry i przekazać je do serwisów. 1000 linii kodu? Bzdura! Odebrać dane, zwalidować, przekazać do serwisu, przekonwertować na viewModel i wypluć(widok lub DTO). czyste MCV? Warstwy panowie! :) Warstwy :) Persistenc Layer , Model , Serwis, Validators , Converters. Domyślny routing może ssać w większych apkach – rozbijamy na mniejsze jednostki i korzystamy z własnego routingu. No i w sumie mamy to.
MIBA,
Tak, ale takie podejście nie bardzo pomaga w dużych systemach, w których NIE MA tych wymienionych rzeczy :). Od czegoś trzeba zacząć. Przedstawiłem pierwszy krok. I materiał do refleksji.
My w aktualnym projekcie używamy CQRS więc w kontrolerze mamy multum małych akcji z przekierowaniem query / command do mediatora i tyle.
Radosław,
Tak, CQRS rozwiązuje sporo problemów, pokazywałem to na swojej prezentacji na ten temat (dostępna tutaj: http://devstyle.pl/video/ ). Wtedy faktycznie kontroler to kilka linijek, a czasami można pokusić się nawet o zebranie całej “write-side” do JEDNEGO kontrolera.
Jak w takim scenariuszu realizujesz warstwę serwisową? Serwisy mają też po jednej metodzie? Załóżmy, że mamy temat – zarządzanie użytkownikami. Czy wtedy tworzysz osobne klasy wykonujące operacje na użytkowniku (np. AddUserService, DeleteUserService, ViewUserService) ?
Dla każdego commanda mamy odpowiedni handler, który odpowiada za obsługę. Np dla AddUserCommand mam AddUserCommandHandler.
Marek,
Zakładasz, że w systemie jest coś takiego jak “AddUserService” itd, podczas gdy podejść jest więcej. Struktura kontrolerów nie jest odwzorowaniem innych warstw, ona tylko je eksponuje na zewnątrz.
Wpis na czasie, jak patrzę na moje kontrolery to mam mieszane uczucia.
Ale sprawa dla mnie nie jest taka oczywista. Moja apka nigdy nie wyjdzie poza Web (jakbym ją sprzedał drugiemu klientowi to bym urżnął ze szczęścia).
Żeby wywalić z kontrolera, to co w nim nie powinno być (wg. kanonów) musiałbym dumać nad kolejnymi interfejsami, serwisami etc.
Oczywiście cześć rzeczy wyrzucę (bo klient zamówił modyfikację/rozbudowę) i pasuje ale resztę zostawię, bo tylko się narobię a nic nie zyskam.
Zresztą i tak jakaś walidacja ukryta jest w kontrolerze – chociażby w informacji o parametrze metody – czy może być null wymusza wstępną walidację na poziomie frameworku.
Tomasz,
6 lat temu tworzyłem system, gdzie pomyślałem “moja apka nigdy nie wyjdzie poza Web, więc wsadzę wszystko w kontrolery” – dzisiaj wiem, że wiele osób w ten kod zaglądało, i się wstydzę :). Wystarczyło, że musiałem wystawić jakieś ‘api’ dla innych systemów, i zrobiła się z tego wielka brzydka kupa. Wtedy obiecałem sobie, że już nigdy tak nie pomyślę. Tym bardziej że narzut na zrobienie czegoś lepszego jest tak naprawdę niewielki.
Wiesz, ja mam ten luksus, że jestem właścicielem kodu źródłowego więc nikt nie zajrzy, chyba, ze po mojej smierci (bedzie takie zastrzezenie u notariusza). Powodów, żeby kodów nie oglądali jest więcej :-D (np. odsylanie widoku czesciowego w html)
” narzut na zrobienie czegoś lepszego jest tak naprawdę niewielki”
ale nie na tym etapie, musiałbym teraz wymyslac interfejsy, testowac, a nikt za to nie zaplaci i ryzykowac bledy, za ktore moga mnie skasowac w PLN. Apka byla kiedys robiona baaardzo po taniosci i na wariata (ratowanie pewnego wdrozenia innej firmy) i teraz nie wytlumacze klientowi, “hej, doplaccie to wam uporzadkuje kod” bo oni nie rozumieja co to jest kod zrodlowy :-)
Ale pomijajac to sa jeszcze dwie wazne kwestie dla mnie – jedna ludzka – naprawde w tym kodzie nie chce mi sie juz grzebac – jestem zmeczony projektem (i generalnie c#); i druga filozoficzna – czy w kodzie TRZEBA zawsze stosowac wzorce. Skoro wzorce maja ulatwic zrozumienie kodu a ja go rozumiem, to po co mam na sile stosowac pewne rozwiazania? Inni tez zrozumieja kod – po prostu kontroler jest troche spuchniety, ma wiecej parametrow ale idzie to ogarnac. Nie jest to idealne, ale dziala.
Troche rozwazania na zasadzie jak dlugo polerowac stol – jak jest wystarczająco gładki i szklanka sie nie przewraca, to można sobie odpuścić. Zwlaszcza, ze to stoł za pol ceny i na pare lat.
Teraz robie maly projekt i stwierdzilem – “pierdziele” – nie bawie wie w nadmierne rozdzielanie, separowanie etc, to jest tak male i nie bedzie rozbudowywane, ze robie tak aby bylo szybciej. Nawet odpuscilem IoC – jakby mialo byc rozbudowywane, to w godzine bedzie mozna to przerobic.
Oczywiscie duzy i rokujacy projekt musialby byc przemyslany, bo obecne moje podejscie trzeba umiejetnie stosowac, zeby faktycznie nagle nie obudzic sie z kupa w kodzie.
Jak pojde do korpo, to skonczy sie luz :-D
TOMASZK-POZ, “czy w kodzie TRZEBA zawsze stosowac wzorce” – nie, nie trzeba. Tylko nie mówmy wtedy, że je stosujemy. I nie nazywajmy siebie inżynierami.
Z drugiej strony – zainstalowanie i skonfigurowanie IoC to jakieś 10 minut roboty, mniej niż zajmie pisanie wszystkich “new” nawet w malutkim projekcie. Podobnie ma się rzecz z pisaniem kodu – co za różnica, czy napiszemy go w klasie serwisu, czy w kontrolerze? Żadna! Więc po co go pisać w kontrolerze? A utworzenie nowej klasy to jakieś 15 sekund.
“Tylko nie mówmy wtedy, że je stosujemy. I nie nazywajmy siebie inżynierami.” przepraszam a co ma piernik do wiatraka?
Projektanci pierwszych rakiet nie mieli jeszcze w tym temacie wzorców, a rakiety zbudowali. Byli inżynierami czy coś mnie ominęło? Inżynier tworzy coś nowego, nieraz w nowym obszarze (np. piramidy w Egipcie) i musi być kreatywny a nie kopistą wzorców. Sama nazwa “wzorce” mówi o powtarzalności sytuacji, granic, a inżynieria to przekraczanie granic.
Gdyby tak nie było, nie moglibyśmy do siebie pisać i siedzielibyśmy w jaskiniach.
“Z drugiej strony – zainstalowanie i skonfigurowanie IoC to jakieś 10 minut roboty, mniej niż zajmie pisanie wszystkich “new”
Ten drugi projekt to jest tak male cos, ze tam nawet nie bedzie new w kontrolerze, bo bedzie singleton serwisu + lock.
Inaczej – po co mam korzystać z czegoś, co nie ma sensu? Nie można ślepo naśladować wzorce.
“Podobnie ma się rzecz z pisaniem kodu – co za różnica, czy napiszemy go w klasie serwisu, czy w kontrolerze? ”
Ano taka, ze jak dochodza nowe rzeczy (np. cos nowego splywa ze strony), musze zmieniac definicje interfejsu, definicje metod implementujących + ew. deklaracje w IoC, zas majac mala logike w kontrolerze po prostu modyfikuje w jednym miejscu.
Zauwaz, ze cala aplikacja miesci sie w domenie net, nie ma wywolywania warstwy przez WCF.
Mozna traktowac MVC jako integralna czesc projektu, bo ja wiem, ze TEN projekt nie wyjdzie poza WWW.
Wzorce maja pomagac, a w TYM konkretnym przypadku nie pomoga (chociaz nie zaszkodza), ale dodadza roboty.
Jakby projekt mial spuchnac o uslugi (sms, mail) etc, to bym to przerobil, ale tak naprawde sa wzniejsze rzeczy, na ktore warto poswieci energie, np. ulepszenie UI (to widzi klient, z tym ma stycznosci).
Teoretycznie to nalezaloby zawsze zrobic jak nalezy (np. pokrycie testami wszystkiego) a praktycznie robi sie najwazniejsze rzeczy.
Podobnie inżynier – optymalizuje gdzie to ma sens (np. grubosc farby na poszyciu samolotu) a odpuszcza gdzie indziej (grubosc raczki od spluczka w toalecie samolotu).
W tym konkretnym projekcie ma to ten plus, ze jak po kolejnych modyfikacja klient juz nic nowego nie bedzie chcialnastapi ustabilizowanie produktu), to bedzie to sobie dzialac (mimo niedoskonalosci w srodku).
Jak znajde drugiego i kolejnego klienta na system to wtedy sie pomysli o przerobkach, bo drugi czy trzeci klient to takze dodatkowe pieniadze w przyszlosci (jakies przerobki, raporty), wiec przy okazji mozna porzadkowac.
Mozna napisac, ze projekt jest przed solidnym refaktoringiem a klienci zdecyduja porfelem.
I na koniec napisza jeszcze inaczej – zamiast poswiecac czas na robienie porzadkow wole zrobic nowy, moze ciekawszy projekt, bo ten mi sie znudzil.
Przypomnial mi sie projekt pewnej firmy (system zwiazany z pojazdami – tyle moge zdradzic), zastosowali sam miod – nhibernate, wpf, testy, tegie glowy z uczelni (i pewnie wzorce), ale co z tych technologii jak: instalajacja byla koszmarna, UI jakis taki do kitu, niestabilny, raportowanie to byla tragedia – raport generowal sie kilkanascie minut. Para poszla tam, gdzie nie trzeba – zabraklo pomyslunku w miejscu istotnym dla klienta. To byl pol-prototyp, produkt sie nie przyjal, wdrozenia (serwis) byly kosztowne (wyjazdy, poprawki, roboczogodziny). Ja bym sie nie bal braku wzorcow w paru miejscach.
Dlatego ja staram sie (nie zawsze wychodzi) aby dzialalo fajnie, bez padów, a jak są rokowania na dalszy rozwoj, to moge upiekszac i porzadkowac wnętrze. Na poczatkowym etapie projekty nie sa jakies wielkie, wiec etap uporzadkowania mozana odwlec i czesto do tego nie dochodzi. O taka specyfika tego, czym sie zajmuje – projekty jednostrzałowce :-D
Sorry za literowki, jest troche pozno.
Twórcy pierwszych rakiet mieli wzorce – statyka, dynamika, aerodynamika, balistyka istniały już dużo wcześniej, i musieli korzystać z ich doświadczeń pełnymi garściami. Mieli też prawa fizyki, które sprawiały, że nie dało się po prostu zrobić rakiety z powiązanej sznurkami tektury bo “klient i tak nie rozumie, i nie obchodzi go jak to działa”. Klienta oszukasz, fizyki nie oszukasz – to musiało naprawdę działać i trzymać się dobrych praktyk inżynierskich, aby wykonanie było w ogóle możliwe.
Przekraczanie granic fuszerki i bycie “kreatywnym” w tworzeniu spaghetti kodu metodą kopiuj-wklej, najeżonego ifami i ze znacznie utrudnioną możliwością wprowadzania zmian (bo tak generalnie wygląda kod bez wzorców) to nie jest żadna inżynieria, to jest coś wręcz do niej przeciwnego. Stosowanie wzorców nie jest przykładem braku “kreatywności” lecz dobrej inżynierskiej praktyki, która pozwala rozwiązywać powtarzalne problemy w zrozumiały dla wszystkich fachowców sposób; pozwala skupić się na nowych, rzeczywistych problemach, zamiast na wynajdowaniu koła na nowo, albo co gorsza rozwiązywaniu problemu metodą siłową.
Nie twierdzę bynajmniej, że wzorce trzeba stosować na siłę wszędzie, ale ich niestosowanie jest na ogół poważnym błędem. Pewne uproszczenia i odejścia od wzorców też czasem mogą mieć sens, ale jedno jest pewne – jeśli w kontrolerach mamy jakąkolwiek logikę poza obsługą żądań użytkowników, to znaczy, że nie stosujemy wzorca MVC, bo przeczymy jego definicji. Takie są po prostu fakty.
Dawid,
“jeśli w kontrolerach mamy jakąkolwiek logikę poza obsługą żądań użytkowników, to znaczy, że nie stosujemy wzorca MVC”.
Możesz przytoczyć która część definicji MVC zabrania używania logiki w kontrolerach?
Statyta, dynamika to nie wzorce, to środowisko. Wzorcem byłoby: rakieta musi mieć jeden silnik, zbiornik i trochę szczęścia. I byl taki, który się opierał, ze nie wolno łączyć silników w zespół, ale Koroliow go przekonał do spróbowania. Dzięki temu Gagarin poleciał w kosmos (była moc).
“Przekraczanie granic fuszerki i bycie “kreatywnym” w tworzeniu spaghetti kodu metodą kopiuj-wklej, najeżonego ifami ”
Stosowanie wzorców to nie jest kopiuj- wklej? nawet ide mają generatory do singletonów :-D. Ile razy skopiowałeś konfigurację kontenera i później modyfikowałeś? Przyznaj się :-D
Doprawdy uważasz, że mnożenie interfejsów i wpisów IoC jest lepsze niż trochę więcej if-ów.
Dobry inzynier powie “to zalezy”.
“ze znacznie utrudnioną możliwością wprowadzania zmian” a co jak nie jest znacznie utrudniona?
” jeśli w kontrolerach mamy jakąkolwiek logikę poza obsługą żądań użytkowników,”
Z książki Martina Fowlera “Patterns of Enterprise Application Arch” w wydaniu polskim (ISBN8373617159)- fragmenty:
“Model…Zawiera wszystkie dane i udostępnia wszystkie operacje, które nie są związane z obsługą int. użytkownika. … rozdzielenie widoku od kontrolera nie ma aż tak duzego znaczenia.”
Kontroler jak sama nazwa mówi, jest kontrolerem, a obsługa żądań użytkownika (w sensie http) to wierzchołek góry lodowej.
Ta separacja, którą tak promujesz ma duży sens, gdybym miał kilka widoków/UI (WinForms, Telnet, http, konsola) – wtedy każdy z nich miałby swój natywny kontroler. Ale tu już ocieramy się o kontroler aplikacji.
TOMASZK-POZ, być może IDE mają generatory do singletonów, ale singleton to nie jest wzorzec warty stosowania, a jeśli już jest potrzebny, to problem rozwiązuje kontener IoC.
Nie pisałem nic o mnożeniu interfejsów ani wpisów IoC. Dobry kontener ani nie wymaga interfejsów do działania, ani oddzielnej konfiguracji dla każdej klasy, bo to załatwiają konwencje.
Jeśli chodzi o ify, to przecież nie o IoC chodzi, ale o wzorce, które pozwalają się ifów pozbyć: strategia, metoda szablonowa, łańcuch odpowiedzialności, itd.
Fowler o kontrolerach napisał tyle: “The controller’s job is to take the user’s input and figure out what to do with it.” Szerzej zostało to opisane dużo wcześniej w oryginalnym tekście Pope’a i Krasnera.
U nas był ten sam problem.
Został rozwiązany trochę inaczej. Aktualnie dla każdej “strony”, czyli obszaru obsługiwanego przez jeden controller jest tworzony FactoryObject, który trzymamy dla każdego użytkownika w sesji – zapamiętujemy w nim system uprawnień i dostępów oraz znajduje się w nim sama logika łączenia modelu z kontrolerem – czyli nasz kod. Akcje w kontrolerze ograniczyły się nam do 2-3 linijek.