Ile wysiłku powinienem zainwestować w tworzenie luźno powiązanych projektów?

10

Obecnie uczę się o wzorach projektowych.

Myślę, że większość ludzi zgodzi się, że te wzorce są świetnymi narzędziami, ale powinny być stosowane z umiarem, a nie jako odpowiedź na wszystko. Nadmierne ich użycie nadmiernie skomplikowałoby aplikację, przynosząc niewielkie korzyści. Wzory powinny być stosowane tylko tam, gdzie mogą być najlepszym rozwiązaniem lub pomóc w stworzeniu dobrego rozwiązania (zgadzasz się?).

Mając to na uwadze:

Książka, którą czytam (Head First Design Patterns) często podkreśla znaczenie luźnego łączenia . To luźne sprzężenie uzyskuje się zgodnie z następującymi zasadami, takimi jak „program do interfejsu, a nie implementacja” i „hermetyzuj to, co różne”.

Zasadniczo większość wzorców, których nauczyłem się do tej pory, istnieje głównie po to, aby umożliwić swobodne łączenie projektów, a tym samym większą elastyczność.

Rozumiem znaczenie i zalety luźnego sprzężenia.

Ale moje pytanie brzmi: ile wysiłku należy zainwestować w tworzenie luźno powiązanych, elastycznych projektów?

Ci, którzy sprzeciwiają się wzorcom projektowym, twierdzą, że koszty ich stosowania często przewyższają korzyści. Poświęcasz dużo czasu na tworzenie luźno sprzężonego projektu z wykorzystaniem pewnego wzorca, w którym w rzeczywistości - luźne połączenie, „programowanie interfejsu, a nie implementacja” i wszystkie te zasady - mogą nie być tak ważne.

Chciałbym wiedzieć, ile wysiłku powinienem włożyć w tworzenie dodatkowych poziomów abstrakcji i projektów, tylko po to, aby moja aplikacja mogła przestrzegać zasad OO, takich jak luźne sprzężenie, programowanie interfejsu itp. Czy naprawdę warto to? Ile wysiłku powinienem w to włożyć?

Aviv Cohn
źródło
Niektórzy ludzie poświęcają temu całe życie, na przykład Kent Beck, ludzie, którzy to docenią, dostrzegą Twój wysiłek.
Niing

Odpowiedzi:

14

Głupia odpowiedź: im więcej to robisz, tym mniej wysiłku.

Zasadniczo budowanie luźno sprzężonego kodu nie jest naprawdę trudne. Z drugiej strony jest szkolenie umysłu do myślenia w kategoriach luźnego sprzężenia. I cóż, uważam, że jest to całkowicie tego warte.

Załóżmy, że podejmujesz pracę polegającą na iteracyjnym podejściu do tworzenia oprogramowania, zgodnie z zaleceniami większości osób w ciągu ostatnich 20 lat. Budujesz coś, co pięknie działa w iteracji 1. W iteracji 2 budujesz na niej, dodając nowe funkcje i nieco zginając jedną lub dwie rzeczy, gdy nie pasują one do twojej iteracji 1 koncepcja tego, jak rzeczy powinny działać . Teraz nadchodzi iteracja 3, a okazuje się, że wymagania wymagają ponownego przemyślenia podstawowej architektury. Jak rozerwać kod na części i odbudować go, nie wracając do kwadratu?

To się stało. Dużo. Powoduje to, że projekty się spóźniają lub wszyscy zbytnio boją się robić to, co należy zrobić w późniejszych iteracjach. W najlepszym przypadku dostajesz Big Ball of Mud. Rzeczy takie jak luźne połączenie i zasada pojedynczej odpowiedzialności w znacznym stopniu łagodzą te problemy. Dlatego propagowane są zasady SOLID - naprawdę pomagają.

Przekonasz się, że po tym, jak masz kilka luźno połączonych wzorów pod pasem, zaczyna się naturalnie. Wzory to rzeczy, które ludzie uznali za działające, więc je udokumentowali. Są użytecznymi narzędziami, które naturalnie przychodzą z czasem.

Matthew Flynn
źródło
5

Wymagany wysiłek nie zawsze będzie wystarczający; Myślę, że lepszym miernikiem byłby stosunek wysiłku do wypłaty. Jednak ustalenie, jaka może być wypłata, nie zawsze jest proste i wolne od błędów.

Należy pamiętać o wzorcach, które zwiększają elastyczność, ponieważ włożenie ich kosztuje trochę wysiłku, a jeśli nie zobaczysz dokładnie, dlaczego ich potrzebujesz, możesz mieć komplikację w kodzie, ale nigdy jej nie użyjesz. Jeśli elastyczność nigdy nie jest elastyczna, równie dobrze może jej nie być.

Jest jedna rzecz, którą powinieneś zawsze robić (lub tak blisko, jak to rozsądnie można uzyskać): przekształcenie poszczególnych komponentów oprogramowania (obiektów do OOP, funkcji do programowania funkcjonalnego lub proceduralnego) w czarne skrzynki. Czarna skrzynka to coś, czego wnętrza (implementacja) nie znamy ani nie obchodzą nas.

Jeśli nie wiemy ani nie dbamy o to, jak to działa, nie będziemy próbować używać niczego poza interfejsem, więc jeśli implementacja zmieni się bez zmiany interfejsu, ma to znaczenie tylko w czarnej skrzynce - nic poza nią może to mieć wpływ. Ułatwia także myślenie o programie, ponieważ nie musisz mieć w pamięci każdego szczegółu całego programu. Im więcej szczegółów możesz legalnie zapomnieć, tym więcej miejsca w twojej głowie na szczegóły, które mają znaczenie.

Myślę, że źle zrozumiałeś obiekcje wobec wzorów; Nie jestem ich fanem, ale bardzo podoba mi się zasada luźnego łączenia i programowania interfejsu. Moim największym sprzeciwem wobec nich jest to, że są prezentowane jako gotowe rozwiązanie, które nie zachęca do zrozumienia zasad leżących u ich podstaw, ale raczej zachęca do zapamiętywania szczegółów. Nie zamierzam zalecać, żebyś ich nie studiował, ale kiedy to zrobisz, pamiętaj, że zrozumienie stojących za nimi zasad jest ważniejsze niż specyfika konkretnego wzoru.

Jednym z zastrzeżeń do projektowania wzorów jest to, że są one „oczywiste” lub że „używałem ich, zanim o nich usłyszałem, ale nie pod tą nazwą”. Powodem, dla którego ludzie mogą sprzeciwić się temu, lub twórcy wzorców projektowych, mogą przede wszystkim je wymyślić, jest to, że rozumieli za nimi zasady.

Korzystanie z czarnych skrzynek porządkuje kod w rozsądny sposób i zazwyczaj nie kosztuje dużo (jeśli w ogóle); używaj ich wszędzie. Korzystanie z wzorca projektowego lub innej techniki, która dodaje warstwę abstrakcji lub komplikuje rzeczy, ma swój koszt; nigdy nie używaj ich tylko dlatego, że są ładne, schludne lub sprytne; używaj ich, gdy pasują do problemu, a koszt jest opłacalny, aby uzyskać korzyść.

Michael Shaw
źródło
4

Twoje pytanie wydaje się równoznaczne z luźnym sprzężeniem z wzorami projektowymi, ale są one naprawdę oddzielne, ale powiązane ze sobą.

Luźne sprzężenie jest podstawową kwestią w programowaniu. Gdy tylko odeszliśmy od kodu maszynowego i wprowadziliśmy języki symboliczne, programy zostały luźno powiązane z ich wykonywaniem. Itp.

Sam OO jest w zasadzie modelem do tworzenia luźno sprzężonych komponentów. Enkapsulacja powoduje, że wnętrze obiektów jest luźno sprzężone z innymi obiektami. Programowanie funkcjonalne może nawet bardziej, ponieważ wykonywanie funkcji jest luźno połączone z wykonywaniem innych funkcji w skrajności.

Niemal z definicji każdy projekt, który wykonujesz, będzie wymagał luźnego sprzężenia. Istnieją sposoby, można zorganizować mieć rzeczy ściśle sprzężone przypadkowo, ale widziałem tylko kilka przypadków, gdy ktoś faktycznie zaprojektował system, aby być bardziej ściśle sprzężone.

(Od czasu do czasu pracuję z kimś, kto chce uniemożliwić przyszłym programistom dokonywanie wyborów projektowych poprzez osadzanie statycznych odniesień do określonych artefaktów szkieletu, wymuszając ich użycie - celowo. Ale to naprawdę wyjątek. Większość projektów obraca się wokół rozbijania elementów na modułowość i użyj ponownie w wielu kontekstach).

Obrabować
źródło
3

Wzory powinny być stosowane tylko tam, gdzie mogą być najlepszym rozwiązaniem lub pomóc w stworzeniu dobrego rozwiązania (zgadzasz się?).

Widzę wzorce projektowe wyłącznie jako szczegóły implementacji. Jeśli udokumentujesz swoje publiczne interfejsy API i program w tej dokumentacji, na ogół nie będzie to miało znaczenia (ani nie wpłynie na ciebie), gdzie masz wzorce projektowe. To znaczy, nie mówisz: „Mam tutaj wzór mostu, a na nim zaimplementuję gościa”. Zamiast tego jest to: „ta klasa będzie miała różne implementacje w różnych systemach operacyjnych, więc będzie implementowana przy użyciu wzorca mostu”. Następnie, gdy go używasz, jesteś obojętny na to, że jest on implementowany jako most - ponieważ patrzysz na publiczny interfejs API, a nie na wzorzec mostu.

ile wysiłku należy zainwestować w tworzenie luźno powiązanych, elastycznych projektów?

Luźne sprzężenie można osiągnąć, przestrzegając prostego zestawu zasad. Jeśli je uszanujesz, Twój kod będzie (więcej) luźno powiązany, gdy go napiszesz (tzn. Każdy wysiłek jest już częścią procesu programowania).

Wśród zasad (lista nie jest wyczerpująca):

  • zdefiniuj swoje interfejsy, myśląc (lub pisząc) kod klienta (jak klasa będzie używana), a nie to, co zrobi klasa (tj. projektując interfejs, a nie implementację)
  • „powiedz, nie pytaj”
  • konstruować obiekty z już utworzonych części
  • przekaż do konstruktora rzeczywiste obiekty, których będziesz używać (nie fabryki dla członków, parametry dla fabryk parametrów ani nic podobnego).
  • OSUSZANIE (jeśli masz dwie linie, które pojawiają się w tej samej kolejności w dwóch miejscach, wypakuj je do osobnej funkcji i tak dalej).
  • Jeśli tworzenie obiektu jest bardziej złożoną operacją, zaimplementuj tworzenie części pośrednich jako metody / klasy fabrycznej (tj. Nie w treści konstruktora).
  • YAGNI (twórz rzeczy, których potrzebujesz, nie wcześniej).

Zasady te są przestrzegane w różny sposób, w zależności od języka, metodologii rozwoju stosowanej przez zespół (np. TDD), ograniczeń budżetu czasu itp.

Na przykład w Javie dobrą praktyką jest zdefiniowanie interfejsu jako interfacei napisanie na nim kodu klienta (następnie utworzenie instancji interfejsu za pomocą klasy implementacji).

Z drugiej strony w C ++ nie masz interfejsów, więc możesz pisać interfejs tylko jako abstrakcyjną klasę podstawową; Ponieważ w C ++ używasz dziedziczenia tylko wtedy, gdy masz na to duże wymagania (i jako takie unikniesz narzutu niepotrzebnych funkcji wirtualnych), prawdopodobnie nie zdefiniujesz interfejsu osobno, tylko nagłówek klasy).

Ci, którzy sprzeciwiają się wzorcom projektowym, twierdzą, że koszty ich stosowania często przewyższają korzyści.

Myślę, że robią to źle. Jeśli piszesz luźno sprzężony (i SUCHY) kod, integracja wzorców projektowych wymaga minimalnego dodatkowego wysiłku. W przeciwnym razie będziesz musiał dostosować kod do implementacji wzorca projektowego.

Jeśli musisz wprowadzić wiele zmian w celu zaimplementowania wzorca projektowego, problemem nie jest wzorzec projektowy - to podstawa kodu jest monolityczna i ściśle powiązana. Jest to zły / nieoptymalny problem projektowy, a nie problem z wzorami projektowymi.

Chciałbym wiedzieć, ile wysiłku powinienem włożyć w tworzenie dodatkowych poziomów abstrakcji i projektów, tylko po to, aby moja aplikacja mogła przestrzegać zasad OO, takich jak luźne sprzężenie, programowanie interfejsu itp. Czy naprawdę warto to? Ile wysiłku powinienem w to włożyć?

Twoje pytania zakładają (niepotwierdzone) założenie, że jedyną korzyścią z luźnego sprzężenia jest możliwość łatwego wdrożenia wzorców projektowych. Nie jest.

Wśród zalet luźnego połączenia są:

  • elastyczność refaktoryzacji i przeprojektowania
  • mniej zmarnowanego wysiłku
  • testowalność
  • zwiększona możliwość ponownego użycia kodu
  • prostota projektu
  • mniej czasu spędzonego w debuggerze

... i kilka innych, które nie przychodzą mi teraz do głowy.

utnapistim
źródło