Czy zbyt duża abstrakcja może być zła?

46

Jako programiści uważam, że naszym celem jest dostarczanie dobrych abstrakcji na temat danego modelu domeny i logiki biznesowej. Ale gdzie ta abstrakcja powinna się skończyć? Jak dokonać kompromisu między abstrakcją a wszystkimi jej zaletami (elastyczność, łatwość zmiany itp.) Oraz łatwością zrozumienia kodu i wszystkimi jego zaletami.

Wydaje mi się, że mam tendencję do pisania zbyt abstrakcyjnego kodu i nie wiem, jak dobry jest; Często piszę to tak, jakby to była mikrostruktura, która składa się z dwóch części:

  1. Mikromoduły, które są połączone w mikrośrodowisko: moduły te można łatwo zrozumieć, opracować i utrzymywać jako pojedyncze jednostki. Ten kod zasadniczo reprezentuje kod, który faktycznie wykonuje czynności funkcjonalne, opisane w wymaganiach.
  2. Kod łączący; teraz tutaj uważam, że jest problem. Ten kod bywa skomplikowany, ponieważ czasami jest bardzo abstrakcyjny i na początku trudno go zrozumieć; wynika to z faktu, że jest to czysta abstrakcja, podstawa w rzeczywistości i logika biznesowa są wykonywane w prezentowanym kodzie 1; z tego powodu ten kod nie powinien zostać zmieniony po przetestowaniu.

Czy to dobre podejście do programowania? Że to, mając zmieniający się kod bardzo rozdrobniony w wielu modułach i bardzo łatwy do zrozumienia, a niezmienny kod bardzo złożony z abstrakcji POV? Czy cały kod powinien być jednolicie złożony (to znaczy kod 1 bardziej złożony i powiązany i kod 2 prostszy), tak aby każdy, kto go przejrzy, zrozumiałby go w rozsądnym czasie, ale zmiana jest droga lub rozwiązanie przedstawione powyżej jest dobre, gdzie „zmiana kodu” jest bardzo łatwa do zrozumienia, debugowania, zmiany, a „linkowanie kodu” jest dość trudne.

Uwaga: nie chodzi o czytelność kodu! Zarówno kod 1, jak i 2 są czytelne, ale kod 2 ma bardziej złożone abstrakcje, podczas gdy kod 1 zawiera proste abstrakcje.

m3th0dman
źródło
3
Komentarze i jasne nazwy są wymyślane, aby przyspieszyć czas potrzebny na zrozumienie złożonego kodu. Jest całkowicie w porządku, aby kod niższego poziomu był bardziej złożony; na pewnym poziomie, prawie na pewno i tak nazywasz znacznie bardziej złożonym i znacznie niższym poziomem.
DougM
25
„Zbyt dużo” jest z definicji złe.
Jon Purdy
@JimmyHoffa: Muszę zachować te typy na swoim miejscu. I nie bądź zazdrosny - nie mogę pisać Haskell cały dzień. W rzeczywistości jest to głównie PHP, JavaScript i OCaml.
Jon Purdy

Odpowiedzi:

78

Pierwsze słowa TC ++ PL4:

Wszystkie problemy w informatyce mogą być rozwiązane przez inny poziom pośredni, z wyjątkiem problemu zbyt wielu warstw pośrednich. - David J. Wheeler

(David Wheeler był moim doradcą dyplomowym. Cytat bez ważnego ostatniego wiersza jest czasem nazywany „Pierwszą zasadą informatyki”).

Bjarne
źródło
6
A skąd wiesz, że masz zbyt wiele poziomów pośrednich? Powiedziałbym, że pochodzi z doświadczeniem, ale bardziej doświadczony programista z łatwością rozumie więcej pośrednictwa, dlatego nie widzi problemu ze zbyt dużą liczbą poziomów.
m3th0dman
2
@ m3th0dman - masz odpowiedni poziom abstrakcji, gdy łatwo jest wprowadzić zmiany w przyszłości. Oczywiście możesz również zapytać, skąd wiesz, kiedy to nastąpi, co tylko powtarza cykl pytań w inny sposób.
1
to pytanie jest jak zależne od poziomu programisty. Będziesz miał złożonych programistów, którzy zrozumieją twoją szaloną 8-warstwową architekturę warstw i uznają ją za genialną, podczas gdy inni prości, ale sprawni koderzy uznają ją za absurdalną i spierają się o twoją szaloną 8-warstwową projekt warstwowy ... tutaj dokumentacja się opłaca, nie tylko pozwala ci udokumentować swój kod, ale także pozwala go bronić
Ryan
1
przepraszam za moją ignorancję, ale nie rozumiem „TC ++ PL4”
LastTribunal 18.08.17
30

Tak, zdecydowanie. Chodzi o to, że abstrakcja nie jest idealna. Wszystkie szczegóły warstwy, na której znajdują się abstrakcje, są tam z jakiegoś powodu i może to uprościć wiele rzeczy, ale gdyby ta złożoność w pewnym momencie nie była konieczna, prawdopodobnie nie byłoby jej w ogóle. A to oznacza, że ​​w pewnym momencie każda abstrakcja w jakiś sposób wycieknie.

I na tym polega prawdziwy problem. Kiedy abstrakcje się nie udają, im więcej z nich utworzysz między kodem, który napisałeś, a tym, co się dzieje, tym trudniej jest go rozwiązać i rozwiązać, ponieważ jest więcej miejsc, w których może występować problem. Im więcej warstw, tym więcej musisz wiedzieć, aby je wyśledzić.

Mason Wheeler
źródło
1
„A to oznacza, że ​​w pewnym momencie każda abstrakcja w jakiś sposób wycieknie.”: Prawda. Lepsza abstrakcja to taka, która rzadziej przecieka.
Giorgio
11
W świetle odpowiedzi Bjarne'a (i odwołując się do strony wiki Davida Wheelera ), być może możesz zmienić przypisanie cytatu? :)
congusbongus
2
@CongXu: Btw Poszedłem na to z drugiego końca: szukam w „cytatach Bjarne Stroustrup” i nie znalazłem ani jednego odniesienia do Bjarne, który wypowiedział zdanie „dodanie kolejnej warstwy pośredniej” ... Oczywiście nie rozstrzygające, ale robi to bardzo mało prawdopodobne, że to on pierwszy to wypowiedział.
Marjan Venema
1
Więcej warstw abstrakcji oznacza proste abstrakty, a tym samym mniej przecieków na abstrakcji. Zatem w sformułowaniu matematycznym (oczywiście bez żadnego dowodu) suma wycieku abstrakcji może być stała wraz ze zmianą liczby poziomów abstrakcji.
m3th0dman
2
Kiedyś naiwnie doradziłem seniorowi, aby dodać abstrakcję tam, gdzie nie była potrzebna. Nigdy nie był używany poza tym, co chciałem zrobić w pierwszej kolejności.
Cees Timmerman,
15

Tak, absolutnie.

Analogią, którą lubię używać do wyjaśnienia programowania, jest krawiec. Wykonując garnitur, dobry krawiec zawsze pozostawia niewielką ilość materiału w strategicznych miejscach odzieży, aby umożliwić jej przyjęcie lub wyjęcie bez zmiany ogólnego kształtu lub struktury.

Dobry krawiec nie pozostawia ryz materiału na każdym szwie, tylko na wypadek, gdybyś wyhodował trzecie ramię lub zajdzie w ciążę. Zbyt dużo materiału w niewłaściwych miejscach spowoduje źle dopasowane i źle noszone ubranie, dodatkowy materiał po prostu przeszkadza w normalnym użytkowaniu. Za mało materiału, a ubranie jest podatne na łzy i nie będzie można go zmienić, aby poradził sobie z drobnymi zmianami w sylwetce użytkownika, wpływającymi na sposób ubierania się ubrania.

Być może któregoś dnia nasz Dobry Krawiec będzie miał za zadanie uszyć sukienkę tak mocno, że będzie musiał wszyć ją w nią. Być może nasz Dobry Krawiec jest proszony o wykonanie stroju macierzyńskiego, w którym styl i dopasowanie mają pierwszeństwo przed wygodą i możliwością rozbudowy. Ale przed podjęciem któregokolwiek z tych specjalnych zadań dobry krawiec byłby na tyle mądry, aby uświadomić wszystkim o kompromisach osiąganych w celu osiągnięcia tych celów.

Czasami te kompromisy są właściwą drogą, a ludzie są gotowi zaakceptować ich konsekwencje. Ale w większości przypadków podejście polegające na pozostawieniu miejsca, w którym liczy się najbardziej, przeważy nad wszelkimi dostrzeganymi korzyściami.

Odnosząc to do abstrakcji. Absolutnie możliwe jest posiadanie zbyt wielu warstw abstrakcji, tak jak możliwe jest posiadanie zbyt małej ilości. Prawdziwą sztuką programisty, podobnie jak naszych przyjaciół-krawców, jest pozostawienie tego, co najważniejsze.

Wracając do tematu.

Problemem z kodem zwykle nie jest abstrakcja, lecz zależności. Jak już wskazałeś, to jego kod, który łączy dyskretne obiekty, jest problemem, ponieważ istnieje ukryta zależność między nimi. W pewnym momencie komunikacja między rzeczami musi być konkretna, ale ocena, gdzie jest ten punkt, zwykle wymaga pewnych domysłów.

Mówiąc, że „Micro” cokolwiek, zwykle oznacza, że ​​dokonałeś nadmiernej granulacji układu obiektu i prawdopodobnie używasz Type jako synonimu tego, co powinno być Danymi . Posiadanie mniejszej liczby rzeczy oznacza również mniej zależności potrzebnych do komunikacji między nimi.

Z tego powodu jestem wielkim fanem asynchronicznego przesyłania komunikatów między systemami. Otrzymujesz dwa systemy zależne od wiadomości , a nie od siebie. Zapewniając mniej ścisłe połączenie między systemami komunikacyjnymi. W tym momencie, jeśli potrzebujesz zależności między systemami, musisz rozważyć, czy masz bity zależne w odpowiednim miejscu (miejscach). I często tak jest, że nie.

Wreszcie skomplikowany kod będzie skomplikowany. Często nie można tego obejść. Ale kod, który ma mniej zależności, jest znacznie łatwiejszy do zrozumienia niż ten, który opiera się na różnych stanach zewnętrznych.

Matt D.
źródło
2
+1 za „Dobry Krawiec nie pozostawia ryz materiału w każdym szwie, tylko na wypadek, gdybyś wyhodował trzecie ramię lub zajdzie w ciążę”. Niestety, często projektujemy oprogramowanie w ten sposób.
Kemoda
Oprócz zrozumiałości można również znaleźć abstrakcję przydatną podczas zginania linii zakrzywionej w linię prostą. Oznacza to, że redukujesz złożoność i / lub entropię dzięki abstrakcji. Być może coś w rodzaju procedury obsługi danych typu Curry, w której golony tłuszcz jest nadal przydatny później.
Cody
13

Uważam, że naszym celem jest dostarczenie dobrych abstrakcji na temat danego modelu domeny i logiki biznesowej.

Mam inne zdanie: naszym celem jest rozwiązanie problemu biznesowego. Abstrakcje to tylko technika organizowania rozwiązania. Inna odpowiedź korzysta z analogii krawieckiej odzieży. Mam inną analogię, o której lubię myśleć: szkielet. Kod jest jak szkielet, a abstrakcje to stawy między kościami. Jeśli nie masz stawów, po prostu kończysz z jedną kością, która w ogóle nie może się poruszać i jest bezużyteczna. Ale jeśli masz zbyt wiele stawów, kończysz się kupą niezdarnej galaretki, która nie jest w stanie samodzielnie wytrzymać. Sztuczka polega na znalezieniu właściwej równowagi - wystarczającej liczby stawów, aby umożliwić ruch, ale nie na tyle, aby nie istniał faktycznie zdefiniowany kształt ani struktura.

Jordan Lev
źródło