Dlaczego należy unikać dziedziczenia?

9

Pamiętam naukę VB4 i przeciąganie przycisku do formularza, dwukrotne kliknięcie tego przycisku i pisanie kodu w module obsługi zdarzeń, którym właśnie zostałem magicznie pobłogosławiony. Pochodząc z QBASIC byłem zachwycony „V” w „VB”, projektant wizualny był dosłownie najlepszą rzeczą od krojonego chleba.

Oczywiście możesz zrobić to wszystko programowo, ale magia „V” była tak pociągająca, że ​​po prostu przeciągnąłeś ten przycisk. Zachęcono nas do skorzystania z tej trasy.

Ale kilka lat temu zacząłem uczyć się o języku C # i frameworku .net i byłem zafascynowany tym, że wszystko, co wiedziałem, właśnie wyszło z okna. W VB6 dzieje się dużo magii, która jest całkowicie odsłonięta w .net: weźmy InitializeComponentsna przykład konstruktory i metodę. W tym ostatnim znajdziesz wszystkie wystąpienia sterowania, które przeciągnąłeś z przybornika, wszystkie zarejestrowane zdarzenia i właściwości ustawione w projektancie.

I w porządku ... tak myślę. Po prostu czuję, że nie mam „tego”, co się dzieje, ten kod, który mogę modyfikować tylko za pośrednictwem projektanta, denerwuje mnie. Za każdym razem, gdy kopiujesz przycisk „Ok” z jednej formy do drugiej (czasami wraz z jego bratem „Anuluj”), w rzeczywistości kopiujesz kod, a to grzech, prawda? OSUSZAJ, nie powtarzaj się, mówi papież .

Odkładając na bok religie i szkoły myślenia, z całej obiektywności, czy nie powinniśmy czerpać formy z form podstawowych, a przycisk „Ok” (i wszyscy jego przyjaciele) żyli w formie podstawowej? Coś takiego, FormBasez czego wywodzi się DialogFormBase; wszystkie klasy tworzone w mgnieniu oka ... przez wpisanie kodu. Przyciski są tworzone w zależności od sposobu utworzenia klasy (tj. Argument enum konstruktora określa, które przyciski mają zostać utworzone), elementy sterujące są rozmieszczone w układzie paneli podzielonych i paneli układu przepływu, wstrzykiwane do postaci jakoContentktóry pasuje do głównego panelu zawartości. Czy nie to robi ASP.net ze stronami wzorcowymi i symbolami zastępczymi treści? Uzyskałem formularz, gdy potrzebuję nowej „strony wzorcowej”, ale ta nowa „strona wzorcowa” nadal pochodzi z podstawowej klasy formularza, więc grafika jest spójna w całej aplikacji.

Dla mnie jest to o wiele więcej ponownego wykorzystania kodu niż cokolwiek innego, co kiedykolwiek zrobiłem z projektantem w WinForms, i nie było to nawet trudne, a kod nie jest zaśmiecony 200-liniową metodą, nad którą nie mam kontroli, mogę umieszczaj komentarze tam, gdzie lubię, nie zostaną one nadpisane przez projektanta. Myślę, że to tylko kwestia wzorców i architektury, co doprowadziło mnie do tego linku: Najlepszy projekt dla formularzy Windows, które będą miały wspólną funkcjonalność , gdzie zdałem sobie sprawę, że byłem na miejscu, z wyjątkiem odpowiedzi tam, sugeruje dokładnie to, co jestem robi, ale to doradzanie przeciwko formy dziedziczenia ponieważ rozważań projektantów. Tego nie rozumiem .z uwagi na względy struktury kodu, szczególnie w odniesieniu do dziedziczenia formy i kontroli, których nie widzę innego powodu, niż dobrze, że psuje projektanta .

Nie wszyscy możemy być po prostu leniwi, więc jakiej części mi brakuje?

Mathieu Guindon
źródło
Nie rozumiem. Czy sugerujesz, że nie powinniśmy używać projektantów wizualnych i pisać ręcznie całego kodu GUI?
Euforyczny
Pre .NET, gdzie był kod kontrolujący wszystkie obiekty przeciągane w formularzu?
JeffO
1
@JeffO, sądząc po wcześniejszym kodzie, z którym miałem do czynienia, powiedziałbym wprost w formularzu, gdzieś pomiędzy wbudowanym SQL ad-hoc a logiką biznesową. Chodzi o to, WinForms to .net; więc jeśli projektant dobrze pracuje z tym, co obecnie pachnie „złym kodem” i „złymi nawykami kodowania” i faktycznie się psuje, kiedy zaczynasz porządkować rzeczy, mówię: porzuć projektanta i traktuj formularze jakimi są (klasy!) .. . i w jaki sposób kodowanie warstwy interfejsu użytkownika w C # jest tak różne od kodowania warstwy interfejsu użytkownika w ExtJS? Robienie tego w WinForms jest „żmudne”, ponieważ „hej, tu jest projektant”? Czy kodowanie interfejsu użytkownika ExtJS jest uciążliwe?
Mathieu Guindon
Cytuję innego użytkownika: CodeCaster; Procedura obsługi zdarzeń ma na celu połączenie GUI z logiką biznesową. Jeśli masz pole tekstowe do wprowadzenia nazwy użytkownika i przycisk Dodaj, kliknięcie przycisku Dodaj powinno jedynie wywołać _userRepository.AddUser (UsernameTextbox.Text). Nie chcesz logiki biznesowej w procedurach obsługi zdarzeń. stackoverflow.com/questions/8048196/…
Pieter B
@Pieter, czy to nie nakłada zależności DAL na warstwę prezentacji?
Mathieu Guindon

Odpowiedzi:

9

Sprzeciwiam się tobie z innym paradygmatem: preferuj kompozycję zamiast dziedziczenia.

Nawet gdy zaczniesz pisać kod GUI ręcznie i użyjesz dziedziczenia do dzielenia się zachowaniem i obrazami między formularzami, napotkasz ten sam problem, co normalny projekt OOP. Powiedzmy, że masz DialogForm, który ma przyciski OK / Anuluj i GridForm, który ma siatkę. Wszystkie okna dialogowe pochodzą z DialogForm i wszystkie formularze za pomocą Grid z GridForm. A potem okazuje się, że chcesz mieć formę z obu. Jak byś to rozwiązał? Jeśli korzystasz z dziedziczenia formularzy, nie ma możliwości jego rozwiązania. Z drugiej strony, jeśli używasz UserControls, możesz po prostu umieścić GridControl w formularzu i gotowe. Nadal możesz używać projektanta z UserControls, więc nie musisz tracić czasu na pisanie żmudnego kodu GUI.

Euforyk
źródło
Cóż, podstawowa forma ma po prostu naprawiony SplitContainer z Panel2, zawierający FlowLayoutPanel BottomPaneli obsługujący typowe zachowania Ok / Cancel / Apply / Close; Panel1 zawiera SplitContainer z naprawionym Panel1 (aby hostować wspólną etykietę „instrukcje” lub menu, paski narzędzi, cokolwiek będzie na górze), a Panel2 trzyma chroniony Panel o nazwie MainContent; każda rzeczywista implementacja decyduje, jak ją wypełnić. Całą zawartość można wprowadzić do implementacji i tak, może to być kontrola użytkownika wykonana z projektantem w celu zaoszczędzenia czasu, dobra uwaga ... ale co z samym formularzem?
Mathieu Guindon
Problemem jest tutaj skala. Po złożeniu wniosku z 10 formularzami, posiadanie jednego wspólnego formularza nie jest niczym wielkim. Problemy zaczynają się, gdy masz aplikację z dziesiątkami lub setkami formularzy. Wtedy będziesz miał formy, które mają wspólne elementy, ale nigdy nie wystarczą, aby utworzyć formę podstawową. W twoim przypadku może się zdarzyć, że chcesz użyć innego układu lub różnych przycisków, ale wszystko inne pozostanie takie samo. I wtedy zaczynają się problemy.
Euforia
2
„Problem ze skalą” nie istnieje, jeśli masz w połowie przyzwoity projekt. Mamy projekt, w którym pracuję z kilkoma setkami formularzy. Używamy dziedziczenia formularzy w celu zapewnienia spójności i wspólnej funkcjonalności, i nigdy nie mieliśmy żadnych problemów z tym
Mason Wheeler
2
@MasonWheeler Miałem dokładnie przeciwne doświadczenie. Próbowaliśmy wykorzystać dziedziczenie formularzy w jednej z naszych aplikacji i za każdym razem, gdy wprowadzaliśmy nawet niewielkie zmiany w formularzach podstawowych, wszystko się załamywało i musieliśmy ręcznie naprawić wszystkie pozostałe formularze. Ponadto projektant zachowuje się dziwnie podczas pracy z odziedziczoną formą.
Euforia
1
@Euphoric: Który projektant? Używamy Delphi, a jego projektant działa dobrze z odziedziczonymi formularzami. I znowu, nie masz tych problemów, jeśli masz dobry projekt . Jeśli niczego nie planujesz, wszystko będzie kruche i łamliwe za każdym razem, gdy go dotkniesz, ale to nie różni się od żadnego innego kodu.
Mason Wheeler
2

Wiele z tego wiąże się z wyborem stosu technologii.

WinForms i WPF mogą być platformami .NET UI, ale różnią się znacznie pod względem sposobu osiągnięcia celu końcowego. Niektóre paradygmaty poruszają się między technologiami w porządku, ale inne nie i nie powinieneś próbować narzucać paradygmatu tylko dlatego, że czujesz, że ma on istnieć w każdym frameworku. Istnieje powód, dla którego MVVM jest nastawiony na WPF / SL, a MVC to osobna koncepcja w ASP.NET od WebForms i tak dalej. Niektóre technologie działają lepiej w przypadku niektórych paradygmatów.

Należy pamiętać, że generowany kod powinien być ogólnie usuwany z obaw związanych z OSUSZANIEM. Chociaż ma zastosowanie w niektórych przypadkach, należy go zignorować. Koncepcje DRY powinny z pewnością pozostać przedmiotem zainteresowania poza warstwą prezentacji, ale w warstwie prezentacji kod generowany jest najczęściej produktem ubocznym środowiska, w którym pracujesz. Nie oznacza to, że nie możesz stosować zasad DRY w swoim podejściu, ale pozostać ostrożny. Czy możesz utworzyć formularz podstawowy i dziedziczyć po nim w nieskończoność? Tak. Czy możesz przeciągać i upuszczać komponenty na nowym formularzu za każdym razem w razie potrzeby? Tak. Skoncentruj się na celu końcowym, unikając potencjalnych wad, ułatwiając refaktoryzację w dół łańcucha dostaw i skalowalność, pracując w ramach wybranego stosu technologii.

Aaron McIver
źródło
0

Państwo może używać dziedziczenie dostarczenie prawidłowo ująć swoje pola i wypełnić odpowiednio. Jeśli chciałbyś użyć dziedziczenia, moją sugestią byłoby mieć formę podstawową i odpowiedni model dla tej formy, a dla każdej pochodnej wyprowadzić zarówno formę, jak i model.

O wiele lepszą alternatywą byłoby grupowanie powiązanych elementów sterujących w jeden lub więcej elementów sterujących UserControl. Potrzebujesz tylko odrobiny logiki, aby umieścić w nim odpowiednie dane.

Sam
źródło
0

Całkowicie zgadzam się z Masonem Wheelerem, chociaż może za trzy lata nawet on zmienił zdanie.

Próbuję poszukać alternatyw dla migracji z plików wykonywalnych Delphi do sieci.

Do tej pory decydowałem się na UNIGUI, ponieważ nadal mogę korzystać z dziedziczenia formularzy. Chciałbym, żeby Delphi miał Mixiny takie jak Dart, ponieważ pozwoliłoby mi to mieć mniej przodków. Ale czuję, że ludzie piszą o wiele więcej kodu w MVC niż w Visual Form Inheritance, ponieważ większość nawigacji, wywołuje reguły sprawdzania poprawności w module danych (applupdates), kryteria filtrowania sql, przeszukuje zestaw danych klienta, wszystko jest napisane w 5 może 6 Tworzą przodków.

Nawet „Z MVC łatwiej będzie ci zostawić Delphi w funkcji” dla mnie nie działa jako argument, ponieważ ludzie zaprosili OOP, wszystko to klasy, właściwości, metody. Potrzebuję więc tylko tłumacza. Naprawdę nie mógł znaleźć żadnej korzyści, może dla stron internetowych, które są znacznie mniej skomplikowane, ale zmieniają się w bazie Daily.

użytkownik3145857
źródło