Czy „C” w MVC jest naprawdę konieczne?


37

Rozumiem rolę modelu i widoku we wzorcu Model-Widok-Kontroler, ale trudno mi zrozumieć, dlaczego kontroler jest potrzebny.

Załóżmy, że tworzymy program szachowy wykorzystujący podejście MVC; stan gry powinien być modelem, a GUI powinien być widokiem. Czym dokładnie jest kontroler w tym przypadku?

Czy to tylko osobna klasa, która ma wszystkie funkcje, które zostaną wywołane, gdy, powiedzmy, klikniesz kafelek? Dlaczego nie wykonać całej logiki na modelu w samym widoku?


1
Osobiście to właśnie robię . Mogą istnieć przypadki, w których nie ma alternatywy dla MVC, ale nie mogę tego znieść.
Mike Dunlavey

10
Trzy słowa ... „Separation Of Concern”.
Travis J

4
Prawie wszystkie programy Windows przed .net używały Doc-View bez kontrolera. To wydaje się być stosunkowo udane.
Martin Beckett

Martin, un (zmień) zdolne monolity.
Niezależny

Odpowiedziałem poniżej, ale dodam, że tak, możesz zbudować aplikację bez odrębnych klas kontrolerów, ale to nie byłby MVC. Zakładasz „podejście MVC”, więc tak, kontrolery odgrywają ważną rolę. Jeśli wybierzesz jakiś paradygmat, który nie jest MVC, całkiem możliwe, że nie będziesz mieć żadnych kontrolerów.
Caleb,

Odpowiedzi:


4

Korzystając z twojego przykładu, kontroler zdecydowałby, co było legalnym posunięciem, czy nie. Kontroler poinformuje widok, jak ułożyć elementy na planszy podczas uruchamiania, korzystając z informacji otrzymanych od Modelu. Jest więcej rzeczy, którymi kontroler może się zająć, ale kluczem jest przemyślenie logiki biznesowej na tej warstwie.

Są chwile, w których wszystko, co robi Kontroler, to przesyłanie informacji tam i z powrotem, jak strona rejestracji. Innym razem Kontroler jest trudną częścią rozwoju, ponieważ na tej warstwie należy wykonać wiele czynności, takich jak na przykład egzekwowanie reguł lub wykonywanie skomplikowanej matematyki. Nie zapomnij o kontrolerze!


36
„Korzystając z twojego przykładu, to Administrator decyduje o legalnym posunięciu, czy nie”. To zły przykład :( taka logika powinna być także w Modelu. W przeciwnym razie logika zostanie podzielona między Kontrolerem a Modelem.
Dime

6
@Dime - model nie powinien zawierać żadnej logiki. Kontroler będzie miał logikę, stąd „kontroluje”.
Travis J

34
@TravisJ Nie do końca się z tym zgadzam. Kontrolowanie nie oznacza umiejętności wykonywania pracy. Chodzi o kontrolowanie tych obiektów, które to robią. Stąd logika wykonywania pracy byłaby w modelu, a kontroler kontrolowałby, którego modelu użyć, aby wykonać niezbędne wymagania akcji itp. Zbyt dużo logiki w kontrolerach moim zdaniem byłoby receptą na blot kontrolera ...
dreza

25
Cały sens OOP polega na tym, aby spójne bity danych i zachowania były utrzymywane razem, a stan wewnętrzny był zamknięty. „Model” modeluje zarówno zachowanie, jak i dane.
Misko

12
-1 Kontroler nie może zawierać logiki biznesowej. Model jest „aplikacją”, przechowuje dane i ma wywoływalne procedury dla wszystkiego, co może się zdarzyć w aplikacji; niekoniecznie wszystkie w jednym pliku lub klasie. Widok wizualizuje stan modelu. Kontroler łączy model / widok z rzeczywistym światem / wejściem. Chcesz „opublikować” aplikację jako aplikację internetową? Potrzebujesz kontrolera obsługującego HTTP i odpowiedniego widoku opartego na HTML. Chcesz interfejs wiersza polecenia do swojej aplikacji? Wystarczy odpowiedni kontroler i widok. Model, logika biznesowa, nigdy się nie zmienia w tych przypadkach.
deceze

39

Dlaczego nie wykonać całej logiki na modelu w samym widoku?

Kontroler to klej, który łączy model i widok razem, a także izolacja, która je rozdziela. Model nie powinien wiedzieć nic o widoku i odwrotnie (przynajmniej w wersji MVC firmy Apple). Kontroler działa jak adapter dwukierunkowy, tłumacząc działania użytkownika z widoku na komunikaty do modelu i konfigurując widok z danymi z modelu.

Użycie kontrolera do oddzielenia modelu i widoku sprawia, że ​​kod jest bardziej przydatny do ponownego użycia, bardziej testowalny i bardziej elastyczny. Rozważ swój przykład szachowy. Model oczywiście obejmowałby stan gry, ale może również zawierać logikę, która wpływa na zmiany stanu gry, na przykład określanie, czy ruch jest legalny i decydowanie o zakończeniu gry. Widok wyświetla szachownicę i pionki oraz wysyła wiadomości, gdy element się porusza, ale nie wie nic o znaczeniu elementów, ruchu każdego elementu itp. Kontroler wie zarówno o modelu, jak i widoku, a także ogólny przebieg programu. Gdy użytkownik naciśnie przycisk „Nowa gra”, jest to kontroler, który mówi modelowi, aby utworzył grę, i używa stanu nowej gry do skonfigurowania planszy. Jeśli użytkownik wykona ruch,

Spójrz na to, co otrzymujesz, zachowując model i oglądaj osobno:

  • Możesz zmienić model lub widok bez zmiany drugiego. Być może będziesz musiał zaktualizować kontroler po zmianie jednego z nich, ale w pewnym sensie jest to jedna z zalet: części programu, które najprawdopodobniej ulegną zmianie, są skoncentrowane w kontrolerze.

  • Model i widok mogą być ponownie użyte. Na przykład możesz użyć tego samego widoku szachownicy z kanałem RSS jako modelu do zilustrowania znanych gier. Możesz też użyć tego samego modelu i zastąpić widok interfejsem internetowym.

  • Łatwo jest pisać testy zarówno dla modelu, jak i widoku, aby upewnić się, że działają tak, jak powinny.

  • Zarówno model, jak i widok często mogą korzystać ze standardowych części: tablic, map, zestawów, ciągów i innych kontenerów danych dla modelu; przyciski, kontrolki, pola tekstowe, widoki obrazów, tabele i inne dla widoku.


1
W oryginalnej architekturze MVC dla aplikacji komputerowych widoki były aktywnymi klasami, obserwującymi model bezpośrednio i odłączonymi od kontrolera.
kevin cline

Problem z wszystkimi odpowiedziami polega na tym, że istnieje tyle interpretacji MVC, ile osób publikujących. Wymienione wyżej korzyści dotyczą tylko jednej konkretnej interpretacji MVC. Jeśli ktoś umieściłby większość logiki w kontrolerze (lub modelu) i miałby wywołać wywołanie View / zainicjować określone wywołania metody na kontrolerze, to sprawia, że ​​kombinacja kontrolera / modelu jest samodzielna i nadaje się do wielokrotnego użytku. W większości przypadków potrzebny jest nowy widok. Nigdy nie musiałem ponownie wykorzystywać widoku. Nawet twój przykład RSS można łatwo obsłużyć za pomocą nowego Widoku, który używa starego z warstwą RSS pomiędzy.
Dunk

2
@Dunk: konieczne jest oddzielenie logiki biznesowej od interfejsu użytkownika, aby logika biznesowa mogła być testowana bezpośrednio.
kevin cline

@Kevin: Całkowicie się zgadzam, logika biznesowa nie należy do interfejsu użytkownika. Jednak kontroler nie musi być częścią interfejsu użytkownika. Mam na myśli to, że istnieje wiele definicji. W definicji jednej osoby kontroler obsługuje naciśnięcia przycisków, podczas gdy inna osoba umieściłaby to jako część widoku. Jeśli widok wie, jak przekształcić działania operatora (tj. Naciśnięcia przycisków / wybory elementów) w żądania aplikacji, wówczas kontroler / model stanie się bardzo przydatny z prawie każdym rodzajem interfejsu użytkownika, który może obejmować GUI, konsolę lub interfejsy sieciowe.
Dunk

1
@Dunk: Przypuszczam, że możesz nazywać wszystko, co lubisz, „kontrolerem”, ale w architekturze MVC kontroler jest zależny, a zatem stanowi część interfejsu użytkownika.
kevin cline

7

Istnieje wiele różnych sposobów realizacji tego ogólnego wzorca projektowego, ale podstawową ideą jest oddzielenie różnych problemów w razie potrzeby. MVC to ładna abstrakcja w tym sensie, że:

Model : Reprezentuje te dane, cokolwiek to może oznaczać
Widok : Reprezentuje interfejs użytkownika, cokolwiek to może znaczyć
Kontroler : Reprezentuje klej, który powoduje, że model i widok wchodzą w interakcję, cokolwiek to może znaczyć

Jest niezwykle elastyczny, ponieważ nie określa wiele. Wiele osób marnuje dużo przepustowości, argumentując szczegóły tego, co może oznaczać każdy element, jakie nazwy należy stosować zamiast nich i czy naprawdę powinny być 3, 2, 4 lub 5 komponentów, ale nie ma sensu pewien stopień.

Chodzi o to, aby oddzielić różne „części” logiki, aby się nie nakładały. Utrzymuj prezentację razem, trzymaj dane razem, logikę razem, komunikację razem. I tak dalej. Do pewnego stopnia, im mniej te obszary zainteresowania się pokrywają, tym łatwiej jest robić z nimi interesujące rzeczy.

To wszystko, czym naprawdę powinieneś się martwić.


3
Klej, klej, podoba mi się ta definicja, jest taka poprawna: cały model powinien być ochrzczony MVG, a ludzie przestaliby drapać się w głowy w poszukiwaniu elegancji, gdzie nie ma nic do znalezienia.
ZJR

1
+1 za „klej”; oznacza to również, że jest to część, która najlepiej nadaje się do wykonania w języku skryptowym (ponieważ mają one tendencję do klejenia).
Donal Fellows,

@DonalFellows Bardzo podoba mi się ta myśl. Coś, co „skleja” 2 różne jednostki razem, wymaga dużej elastyczności, którą promują słabo napisane języki skryptowe (tj. JavaScript)
Zack Macomber

4

Wszystkie dobre odpowiedzi do tej pory. Moje dwa centy to to, że lubię myśleć, że kontroler jest zbudowany przede wszystkim z pytaniami typu Co i gdzie?

  • Zapytano mnie, czy szachy (widok) można przenieść do x. Czy to jest
    dozwolone Nie jestem pewien, ale wiem gdzie i kogo zapytać (model).
  • Coś poprosiło mnie o zapisanie moich danych. Jak do cholery mam to zrobić? Wiem jednak, gdzie zapytać! Jak zapisujemy dane lub gdzie są zapisane, nie mam pojęcia, ale ta klasa repozytorium powinna wiedzieć. Prześlę to i dam sobie z tym radę.
  • Muszę pokazać użytkownikowi bieżącą pozycję szachową, do której model ją przeniósł. Nie jesteś pewien, czy chcę pokazać utwór jako zielony czy żółty? Bah, kogo to obchodzi, wiem, że istnieje widok, który sobie z tym poradzi, więc przekażę im dane i mogą zdecydować, jak to będzie wyświetlane.

Te małe fragmenty są przykładami tego, jak próbuję zapamiętać abstrakcję i koncepcję, którą MVC próbuje przekazać. Co, gdzie i jak są moje trzy główne procesy myślowe.

Co i gdzie => Kontroler Jak i kiedy => Modele i widoki

W gruncie rzeczy moje działania kontrolera wydają się być małe i zwarte, a podczas ich czytania czasami wyglądają jak strata czasu. Przy bliższej inspekcji działają one jako sygnalizator drogowy, kierując różne żądania do odpowiednich pracowników, ale nie wykonując żadnej z samych faktycznych prac.


2

Kontroler może pomóc w wyodrębnieniu interfejsów zarówno Widoku, jak i Modelu, aby nie musieli się o sobie bezpośrednio dowiedzieć. Im mniej obiekt musi wiedzieć, tym bardziej staje się przenośny i testowany jednostkowo.

Na przykład Model może odtwarzać inną instancję samego siebie przez jeden Kontroler. Lub kontroler sieciowy może połączyć ze sobą dwa obiekty Widoku gracza. Lub może to być test Turinga, w którym nikt nie wie, który.


2

To naprawdę wchodzi w grę, gdy masz do czynienia z procedurami obsługi zdarzeń, ale nadal potrzebujesz kontrolera do obsługi interakcji między widokiem a modelem. Idealnie nie chcesz, aby widok wiedział coś o modelu. Pomyśl o tym, czy chcesz, aby jsp bezpośrednio nawiązywał wszystkie połączenia z bazą danych? (Chyba że jest to coś w rodzaju wyszukiwania przy logowaniu). Chcesz, aby widok renderował dane i nie posiadał żadnej logiki biznesowej, chyba że jest to logika wyświetlania, ale nie logika biznesowa.

W GWT uzyskasz czystszą separację dzięki MVP. W widoku nie ma żadnej logiki biznesowej (jeśli jest to zrobione prawidłowo). Prezenter działa jako kontroler, a widok nie ma wiedzy o modelu. Dane modelu są po prostu przekazywane do widoku.


1

Widok dokumentu (tj. Widok modelu) jest standardowym modelem dla większości aplikacji Windows napisanych w MFC, więc musi działać w wielu przypadkach.


1

Rozumiem rolę modelu i widoku we wzorcu Model-Widok-Kontroler, ale trudno mi zrozumieć, dlaczego kontroler jest potrzebny.

Jesteś tego pewien? (Przynajmniej tak, jak pierwotnie opisano) Celem modelu jest model domeny. Widok ma wyświetlać model domeny użytkownikowi. Kontroler ma odwzorować wejście niskiego poziomu na mówienie modelu wysokiego poziomu. O ile mogę stwierdzić, uzasadnienie jest następujące: A) użycie SRP na wysokim poziomie. B) Model został uznany za ważną część aplikacji, więc trzymaj z niego nieważne i szybciej zmieniające się rzeczy. C) łatwo testowalna (i skryptowa) logika biznesowa.

Pomyśl tylko, czy chcesz, aby Twój program szachowy był użyteczny dla osób niewidomych, zamień widok na słyszalną wersję i kontroler współpracujący z klawiaturą. Powiedz, że chcesz dodać gry pocztą, dodaj kontroler, który akceptuje tekst. Wersja netto gry? Kontroler wykonujący polecenia z gniazda wykonałby zadanie. Dodaj do niego ładny render 3d, nowy, fajny widok. Niezbędne zmiany modelu zerowego Szachy są nadal szachami.

Jeśli pomieszasz dane wejściowe z reprezentacją modelu, utracisz tę zdolność. Nagle szachy to nie szachy, to szachy z myszką, które różnią się od szachów z klawiaturą lub połączeniem sieciowym.


0

Myślę, że MVC jest głupi, może w niektórych obszarach działa dobrze, ale osobiście nawet strony internetowe, które piszę, nie są odpowiednie dla mvc. Istnieje powód, dla którego słyszysz frontend, backend i nigdy nie koniec bazy danych lub coś innego

IMO powinien mieć API (backend) i aplikację, która korzysta z API (frontend). Wydaje mi się, że można wywołać żądanie GET kontrolerem (który po prostu wywołuje interfejs API zaplecza), a HTML - widok, ale zwykle nie słyszę, aby ludzie mówili o widoku jako czystym HTML ani modelu będącym interfejsem API zaplecza.

IMO wszystko powinno być solidnym API. W rzeczywistości nie muszą być solidne (jak w czystym i dobrze zbudowanym), ale jego elementy wewnętrzne powinny pozostać prywatne, a aplikacja / frontend / poza interfejsem API nigdy nie powinna mówić o połączeniu z bazą danych ani nie może wykonywać surowych zapytań.

Teraz, jeśli twój kod / projekt wymaga klejenia, to dobrze. Jeśli w twojej grze w szachy jest jakiś znacznik, który możesz edytować, aby skórować GUI, GUI zbiera współrzędne / dane wejściowe i wywołuje MovePiece (srcPosition, dstPostion) (co może zwrócić wartość bool lub enum, aby powiedzieć, czy jest to poprawny ruch, czy nie ) i nie ma problemu z logiką zawartą w modelu, więc na pewno nazwij to MVC. Nadal jednak organizuję rzeczy według klas i interfejsów API i upewniam się, że nie ma klasy zlewu kuchennego, która dotykałaby wszystkiego (ani żadnych interfejsów API, aby wiedzieć o wszystkim).


Witamy w tej opinii, ale ta odpowiedź nie próbuje odpowiedzieć na pytanie PO.
Caleb,

0

Pomyśl o przeglądarce, która wyświetla statyczną stronę internetową. Model to HTML. Widok to rzeczywisty wynik na ekranie.

Teraz dodaj trochę JavaScript. To jest kontroler. Gdy użytkownik kliknie przycisk lub przeciągnie coś, co Zdarzenie zostanie wysłane do JavaScript, decyduje o tym, co należy zrobić, i zmienia bazowy HTML (Model), a przeglądarka / renderer wyświetla te zmiany na ekranie (Widok).

Być może kliknięto inny przycisk, zdarzenie jest wysyłane do jakiegoś modułu obsługi (kontrolera) i może powodować żądanie wysłania dalszych danych do usługi internetowej. Wynik jest następnie dodawany do HTML (Model).

Kontroler reaguje na zdarzenia i kontroluje to, co jest w Modelu, a tym samym to, co jest na ekranie / Widoku.

Cofając się nieco, możesz myśleć o całej przeglądarce jako widoku, a serwerze jako kontrolerze, a danych jako o modelu. Gdy użytkownik kliknie przycisk w przeglądarce zdarzenia wysłanego na serwer (kontroler), gromadzi zasoby jako stronę HTML (model) i wysyła je z powrotem do przeglądarki, aby je wyświetlić (widok)

Na dole serwera, niezależnie od tego, czy jest to asp, php czy java, „kod” (kontroler) odbiera zdarzenie click i odpytuje bazę danych lub repozytorium dokumentów (model) i tworzy HTML. Z punktu widzenia serwera wynikiem wszystkich jego działań jest Widok (HTML) bazowego magazynu danych (Model). Ale z punktu widzenia klienta wynikiem jego żądania do serwera jest model (HTML)

Nawet jeśli zbierzesz JavaScript w HTML lub PHP w HTML, model, widok, kontroler nadal istnieje. Nawet jeśli myślisz o swojej prośbie do serwera i odpowiedzi z serwera jako prostej dwukierunkowej ulicy, nadal istnieje Model, Widok i Kontroler.


-2

Z mojego doświadczenia wynika, że ​​w tradycyjnym programie GUI mvc na pulpicie kontroler jest spaghetti. Większość ludzi nie poświęca czasu na wyróżnienie klasy kontrolera.

książka o gangach czterech mówi:

Wzory projektowe w Smalltalk MVC

Triada klas Model / View / Controller (MVC) [KP88] służy do budowania interfejsów użytkownika w Smalltalk-80. Spojrzenie na wzorce projektowe w MVC powinno pomóc ci zrozumieć, co rozumiemy pod pojęciem „wzór”.

MVC składa się z trzech rodzajów obiektów. Model jest obiektem aplikacji, widok jest prezentacją na ekranie, a kontroler określa sposób, w jaki interfejs użytkownika reaguje na dane wprowadzone przez użytkownika. Przed MVC projekty interfejsu użytkownika miały tendencję do zlepiania tych obiektów razem. MVC oddziela je, aby zwiększyć elastyczność i ponownie wykorzystać.

MVC oddziela widoki i modele, ustanawiając między nimi protokół subskrypcji / powiadamiania. Widok musi zapewniać, że jego wygląd odzwierciedla stan modelu. Za każdym razem, gdy zmieniają się dane modelu, model powiadamia widoki od niego zależne. W odpowiedzi każdy widok ma możliwość samodzielnej aktualizacji. Takie podejście umożliwia dołączenie wielu widoków do modelu w celu zapewnienia różnych prezentacji. Możesz także tworzyć nowe widoki modelu bez przepisywania go.

Poniższy schemat przedstawia model i trzy widoki. (Dla uproszczenia zrezygnowaliśmy z kontrolerów). Model zawiera pewne wartości danych, a widoki definiujące arkusz kalkulacyjny, histogram i wykres kołowy wyświetlają te dane na różne sposoby. Model komunikuje się z widokami, gdy zmieniają się wartości, a widoki komunikują się z modelem, aby uzyskać dostęp do tych wartości.

Podany na pierwszy rzut oka przykład ten odzwierciedla projekt, który oddziela widoki od modeli. Ale projekt ma zastosowanie do bardziej ogólnego problemu: odsprzęganie obiektów, dzięki czemu zmiany jednego z nich mogą wpływać na dowolną liczbę innych, nie wymagając od znanego obiektu znajomości szczegółów innych. Ten bardziej ogólny projekt został opisany przez wzorzec projektu Observer (strona 293).

Inną cechą MVC jest możliwość zagnieżdżania widoków. Na przykład panel sterowania przycisków może być zaimplementowany jako złożony widok zawierający zagnieżdżone widoki przycisków. Interfejs użytkownika dla inspektora obiektów może składać się z zagnieżdżonych widoków, które mogą być ponownie użyte w debuggerze. MVC obsługuje zagnieżdżone widoki za pomocą klasy CompositeView, podklasy View. Obiekty CompositeView działają tak jak obiekty View; widoku złożonego można używać wszędzie tam, gdzie można go użyć, ale zawiera także widoki zagnieżdżone i zarządza nimi.

Ponownie możemy myśleć o tym jako o projekcie, który pozwala nam traktować widok złożony tak, jak traktujemy jeden z jego komponentów. Ale projekt ma zastosowanie do bardziej ogólnego problemu, który pojawia się, gdy chcemy pogrupować obiekty i potraktować grupę jak pojedynczy obiekt. Ten bardziej ogólny projekt jest opisany wzorem wzoru Composite (163). Pozwala stworzyć hierarchię klas, w której niektóre podklasy definiują obiekty prymitywne (np. Button), a inne klasy definiują obiekty złożone (CompositeView), które łączą prymitywy w bardziej złożone obiekty.

MVC pozwala również zmienić sposób, w jaki widok reaguje na dane wejściowe użytkownika bez zmiany jego prezentacji wizualnej. Może chcesz zmienić sposób, w jaki reaguje na klawiaturę, na przykład, czy mają to użycie menu pop-up zamiast przycisków sterujących. MVC hermetyzuje mechanizm odpowiedzi w obiekcie Controller. Istnieje hierarchia klasa sterowników, dzięki czemu można łatwo utworzyć nowy kontroler jako odmianie istniejącej.

Widok wykorzystuje instancję podklasy kontrolera do implementacji określonej strategii odpowiedzi; aby wdrożyć inną strategię, wystarczy zastąpić instancję innym rodzajem kontrolera. Jest nawet możliwe, aby zmienić kontroler przedstawia widok w czasie wykonywania dać pogląd zmienić sposób reaguje na działania użytkownika. Na przykład, widok może być wyłączone tak, że nie akceptuje wejście po prostu przez nadanie jej kontroler, który ignoruje zdarzeń wejściowych.

Relacja View-Controller jest przykładem wzorca projektowego Strategy (315). Strategia to obiekt reprezentujący algorytm. Jest to przydatne, gdy chcesz zastąpić algorytm statycznie lub dynamicznie, gdy masz wiele wariantów algorytmu lub gdy algorytm ma złożone struktury danych, które chcesz obudować.

MVC wykorzystuje inne wzorce projektowe, takie jak Metoda fabryczna (107), aby określić domyślną klasę kontrolera dla widoku i Dekorator (175), aby dodać przewijanie do widoku. Ale główne relacje w MVC są określone przez wzorce projektowe Observer, Composite i Strategy.


1
Wygląda na to, że cały ten post minus dwa pierwsze akapity zostały zaczerpnięte dosłownie z Wzorów projektowych . Sformatowałem tę sekcję jako cytat, aby czytelnicy to zrozumieli - edytuj, jeśli zacytowałem własne akapity.
Caleb,

1
Muszę się nie zgodzić z twoją opinią, że „kontroler kończy się spaghetti w widoku”. Być może różni się w zależności od używanej platformy i frameworka, ale w programach Cocoa i Cocoa Touch jest znacznie bardziej powszechne, aby tworzyć odpowiednie kontrolery niż je pomijać. Jeśli programista Objective-C pominie jedną z kategorii, prawie na pewno ucierpi model.
Caleb,

Jeśli zgodzisz się, że jest to „właściwa” interpretacja MVC, to MVC absolutnie nic nie kupuje. Równie dobrze może to być MV i pominąć C, ponieważ za każdym razem, gdy tworzysz nowy Widok, musisz także utworzyć nowy Kontroler. Po co więc starać się je rozdzielić, chyba że z przyczyn teoretycznych oddzielenia problemów.
Dunk
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.