Co to jest „przedwczesna abstrakcja”?

10

Słyszałem, jak fraza jest rzucana na ziemię i dla mnie argumenty brzmią całkowicie szalone (przepraszam, jeśli mam tu słomkę, to nie moja intencja), ogólnie rzecz biorąc idzie coś w stylu:

Nie chcesz tworzyć abstrakcji, zanim dowiesz się, jaki jest ogólny przypadek, w przeciwnym razie (1) możesz umieszczać w swoich abstrakcjach rzeczy, które nie należą, lub (2) pomijając rzeczy ważne.

(1) Dla mnie brzmi to tak, jakby programista nie był wystarczająco pragmatyczny, przyjęli założenia, że ​​w ostatecznym programie coś by nie istniało, więc pracują z niskim poziomem abstrakcji, problem nie jest przedwczesna abstrakcja, to przedwczesna konkrecja.

(2) Pominięcie ważnych rzeczy jest jedną rzeczą, jest całkowicie możliwe, że coś zostało pominięte w specyfikacji, która później okaże się ważna, rozwiązaniem tego nie jest wymyślenie własnej konkrecji i marnowanie zasobów, kiedy się dowiesz źle się domyśliłem, aby uzyskać więcej informacji od klienta.

Zawsze powinniśmy pracować od abstrakcji po konkrecje, ponieważ jest to najbardziej pragmatyczny sposób robienia rzeczy, a nie na odwrót.

Jeśli tego nie zrobimy, ryzykujemy nieporozumienie klientów i tworzenie rzeczy, które należy zmienić, ale jeśli zbudujemy tylko abstrakcje zdefiniowane przez klientów w ich własnym języku, nigdy nie narażymy się na to ryzyko (przynajmniej nie jest tak prawdopodobne, jak podjęcie strzał w ciemność z pewną konkrecją), tak, możliwe, że klienci zmieniają zdanie na temat szczegółów, ale abstrakcje, których używali, aby przekazać to, czego chcą, nadal są aktualne.

Oto przykład, powiedzmy, że klient życzy sobie stworzenia robota do pakowania przedmiotów:

public abstract class BaggingRobot() {
    private Collection<Item> items;

    public abstract void bag(Item item);
}

Budujemy coś na podstawie abstrakcji, których używał klient, bez wchodzenia w szczegóły z rzeczami, których nie znamy. Jest to niezwykle elastyczne, widziałem to nazywane „przedwczesną abstrakcją”, podczas gdy w rzeczywistości bardziej przedwczesne byłoby zakładanie, jak realizowany jest workowanie, powiedzmy po rozmowie z klientem, że chcą, aby więcej niż jeden przedmiot był pakowany jednocześnie . Aby zaktualizować moją klasę, wystarczy zmienić podpis, ale dla kogoś, kto zaczął od podstaw, może to wymagać dużego przeglądu systemu.

Nie ma czegoś takiego jak przedwczesna abstrakcja, tylko przedwczesna konkrecja. Co jest złego w tym stwierdzeniu? Gdzie są wady mojego rozumowania? Dzięki.

James
źródło
1
Przeciwieństwem przedwczesnej abstrakcji jest YAGNI - nie będziesz jej potrzebował, co moim zdaniem prawie zawsze jest błędne. Prawie. Widziałem, jak to się dzieje, ale to rzadkie. W większości zgadzam się z tym, co tu mówisz.
user1118321

Odpowiedzi:

19

Przynajmniej moim zdaniem przedwczesna abstrakcja jest dość powszechna i była szczególnie wczesna w historii OOP.

Przynajmniej z tego, co zobaczyłem, głównym problemem, który się pojawił, było to, że ludzie czytali typowe przykłady hierarchii obiektowych. Powiedziano im dużo o przygotowaniu wszystkiego, aby poradzić sobie z przyszłymi zmianami, które mogą się pojawić (nawet jeśli nie było szczególnie dobrego powodu, by wierzyć, że tak się stanie). Innym tematem wspólnym dla wielu artykułów przez pewien czas były rzeczy takie jak dziobak, który łamie proste zasady dotyczące „wszystkie ssaki są takie” lub „wszystkie ptaki są takie”.

W rezultacie doszliśmy do kodu, który naprawdę musiał obsługiwać, powiedzmy, zapisy pracowników, ale został starannie napisany, aby był gotowy, jeśli kiedykolwiek wynająłeś pajęczaka lub może skorupiaka.

Jerry Coffin
źródło
Tak więc przedwczesna abstrakcja nie jest aktem abstrakcji, to akt abstrakcji wykraczający poza określony poziom abstrakcji. To ma o wiele większy sens i widziałem, że tak się naprawdę dzieje. Chyba muszę tylko wyjaśnić moim kolegom, że pracuję na poziomie abstrakcji opisanym przez mojego kierownika.
James
1
@James: może to również oznaczać abstrakcję „zbyt wiele” poza specyfikacjami wersji 1.0, ponieważ uważasz, że wiesz o nowych wymaganiach w specyfikacji późniejszej wersji, dlatego już zaimplementuj v1.0 w bardziej ogólny sposób niż niezbędny. Coś, dobrze jest wyobrazić sobie, co może się wydarzyć w późniejszej wersji, ale istnieje również pewne ryzyko nadmiernej inżynierii.
Doc Brown
@DocBrown nazwałbym tę przedwczesną konkrecję. Aktem abstrakcji jest usuwanie fragmentów informacji, a nie ich dodawanie. Jeśli dodajesz więcej do abstrakcji, niż to konieczne, zakładasz coś o niższych poziomach abstrakcji. Chciałbym rozróżnić te dwa, ponieważ myślę, że nazywanie tego „abstrakcją” nie ma większego sensu, ponieważ definicja abstrakcji jest „procesem wydobywania esencji leżącej u jej podstaw”, w której „konkrecja” charakteryzuje się lub należy do bezpośredniego doświadczenia rzeczywistych rzeczy lub wydarzeń ".
James
6

Ważne jest, aby pamiętać, że abstrakcja jest środkiem do celu. Używasz abstrakcji, aby ujednolicić zachowanie w swoim programie i sprawia, że ​​dodawanie nowych klas jest proste w przypadkach, w których spodziewałbyś się, że zostaną dodane nowe klasy, ale tylko wtedy, gdy abstrakcja jest potrzebna w tym momencie.

Nie dodawałbyś abstrakcji tylko dlatego, że może być potrzebna (bez prawdziwej podstawy do myślenia, że ​​będziesz musiał dodać nowe klasy w przyszłości). Właściwą metaforą może być hydraulika. Mamy nadzieję, że zgodzisz się, że 6-kierunkowa rura, która umożliwia przepływ wody w górę / w dół, wschód / zachód, północ / południe, będzie najbardziej elastycznym rodzajem rury, jaką możesz mieć. Teoretycznie możesz użyć 6-kierunkowej rury w dowolnym miejscu, w której jest wymagana rura, i zablokować niepotrzebne kierunki, prawda?

Jeśli spróbujesz naprawić problem z wyciekiem pod zlewem i odkryjesz, że wszystkie odcinki rury to 6-kierunkowe kawałki rur, z frustracją wyciągnij włosy na faceta, który tak zaprojektował. Nie tylko nie wiesz, gdzie jest problem, ale prawie na pewno łatwiej byłoby zacząć od zera we właściwy sposób.

Oczywiście kodowanie nie jest hydrauliką, ale metafora wciąż istnieje. Abstrakcja jest jak użycie tych 6-kierunkowych elementów rurowych. Używaj ich, jeśli szczerze wierzysz, że w najbliższej przyszłości może być konieczne połączenie rur ze wszystkich 6 kierunków. W przeciwnym razie abstrakcja jest po prostu komplikacją, niewiele różniącą się od stosowania wzorca, w którym nic nie jest wymagane, lub używania klasy boga, która próbuje zrobić wszystko. Jeśli nie jest używany i prawdopodobnie nigdy nie będzie używany, ostatecznie dodajesz dodatkową klasę za nic.

Trzeba przyznać, że sztuka pisania programów jest bardzo abstrakcyjna koncepcyjnie. Po prostu warto wspomnieć, że abstrakcje nie istnieją ze względu na to, że są abstrakcją, ale dlatego, że są praktyczne w jakiś realny sposób. Jeśli czujesz potrzebę korzystania z abstrakcji, niech tak będzie, ale nie proś mnie, abym później sprawdził Twoją instalację wodociągową. ;)

Neil
źródło
Wydajesz się nieco classskoncentrowany… Nawet jeśli istnieje wiele klas, abstrakcja nie jest ograniczona do użycia w nich / czerpania z nich korzyści.
Deduplicator
1
@Deduplicator W porządku, ale to nie zmienia mojego punktu widzenia. Jeśli zastosowana zostanie abstrakcja, powinna być natychmiastowa lub powinna być planowaną zmianą w najbliższej przyszłości. Nie ma znaczenia, że ​​mówimy o klasach lub programowaniu funkcjonalnym.
Neil
3

Gdy wiele lat temu dowiedziałem się o analizie i projektowaniu obiektowym, zaczniemy od prostego angielskiego opisu systemu, którego potrzebował klient.

Przeglądając to, każdy rzeczownik (lub fraza rzeczownikowa) byłby uważany za możliwą klasę. Wszelkie czasowniki (lub frazy czasowników) byłyby potencjalnymi metodami na zajęciach. Tak więc „robot workujący” może być klasą BaggingRobot. „Otwórz torbę” może stać się metodą OpenBag.

Po kilku iteracjach zmieni się to w diagram klasowy.

W tym momencie nie ma klas abstrakcyjnych. Klient nie chce abstrakcyjnej koncepcji robota do workowania. Chcą robota, który wkłada rzeczy do worków. Wszystkie klasy są konkretne i mają zestaw metod.

Klasy abstrakcyjne są wprowadzane tylko wtedy, gdy staje się jasne, że:

  • Istnieje kilka podobnych klas, które mogą tworzyć hierarchię.
  • W rzeczywistości mają one coś wspólnego, dzięki czemu klasa podstawowa wykonuje użyteczny cel.

Według mnie „przedwczesna abstrakcja” zakłada, że ​​każdy BaggingRobot musi odziedziczyć po niektórych BaseRobot, a co gorsza, próbować opracować zestaw metod, BaseRobotzanim zdążysz wiedzieć, co jest wspólne dla wszystkich robotów.

Simon B.
źródło
Abstrakcja nie jest synonimem klas abstrakcyjnych. Interfejs jest abstrakcją. Mówię ogólnie o abstrakcjach, a nie tylko klasach abstrakcyjnych. Być może mógłbym to wyjaśnić za pomocą interfejsu. Jeśli rozwiniesz swój system na tej samej warstwie abstrakcji, co twój klient, prawdopodobnie powstanie kilka klas abstrakcyjnych, które należy rozszerzyć, ponieważ język jest naturalnie abstrakcyjny. Praca poniżej tego, co nazywam „przedwczesną konkrecją”, polega na tym, że naiwnie zakładasz, że pomysły klientów dotyczące działania systemu są takie same.
James
Obiekt BaggingRobot, który umieszcza obiekty Item w obiektach Bag, jest już abstrakcją prawdziwego robota workującego, który umieszcza rzeczy w torbach.
user253751
1

Brzmi to trochę tak, jakbyś chciał rozwiązać problem, który jeszcze nie istnieje, i że nie masz uzasadnionego powodu, aby przypuszczać, że wystąpi, chyba że po prostu uważasz, że klient nie ma racji co do żądania. W takim przypadku zaleciłbym większą komunikację niż implementację domyślnego rozwiązania, abstrakcyjnego lub innego. Choć może wydawać się nieszkodliwe po prostu klapnięcie „streszczenia” definicji klasy i czuć się bezpiecznie wiedząc, że możesz ją później rozszerzyć, może się okazać, że nigdy nie musisz tego robić, lub możesz rozszerzyć ją w sposób, który nadmiernie komplikuje projekt linia, poprzez abstrakcję niewłaściwych rzeczy.

Idąc za przykładem, czy wyobrażasz sobie, że to sam robot , pakowane przedmioty lub metoda pakowania zmieniają się wzdłuż linii?

Przedwczesna abstrakcja oznacza po prostu, że nie masz dobrego powodu, aby ją wykonywać, a ponieważ abstrakcje są dalekie od darmowych w wielu językach, możesz ponosić koszty ogólne bez uzasadnienia.

Edytuj Aby odpowiedzieć na niektóre punkty OP w komentarzach, aktualizuję to, aby wyjaśnić i odpowiedzieć w sposób, który nie wymaga długiego łańcucha komentarzy, a dokładniej odnosząc się do punktów (1) i (2).

(1) Dla mnie brzmi to tak, jakby programista nie był wystarczająco pragmatyczny, przyjęli założenia, że ​​w ostatecznym programie coś by nie istniało , więc pracują z niskim poziomem abstrakcji, problemem nie jest przedwczesna abstrakcja, to przedwczesna konkrecja.

(Podkreśl moje). Zakładając, że w ostatecznym programie istniałyby rzeczy, które nie są dokładnie tym, co widzę w twoim przykładzie. Klient poprosił o robota do pakowania przedmiotów. To bardzo specyficzna prośba, choć nieco niejasna w swoim języku. Klient chce robota, który pakuje przedmioty, więc produkujesz parę przedmiotów

public class Item {
/* Relevant item attributes as specified by customer */
}
public class BaggingRobot {
  private Collection<Item> items;

  public void bag(Item item);
}

Twój model jest prosty i precyzyjny, spełnia wymagania określone przez klienta, nie zwiększając złożoności rozwiązania i nie zakładając, że klient chciał więcej, niż prosił. Jeśli przedstawione w ten sposób wyjaśnią potrzebę posiadania przez robota wymiennych mechanizmów workowania, tylko wtedy masz wystarczającą ilość informacji, aby uzasadnić utworzenie interfejsu lub innej metody abstrakcji, ponieważ możesz teraz wskazać konkretną wartość dodaną do system poprzez abstrakcję.

Wręcz przeciwnie, jeśli zaczniesz od abstrakcji od czystego pragmatyzmu, albo spędzasz czas na tworzeniu osobnego interfejsu i wdrażaniu go w konkrecji, albo tworzysz klasę abstrakcyjną, lub dowolny inny sposób abstrakcji, jaki ci się podoba. Jeśli okaże się, że klient jest z tego zadowolony, a dalsze przedłużanie nie jest konieczne, na próżno spędziłeś czas i zasoby i / lub wprowadziłeś do systemu koszty ogólne i złożoność bez żadnego zysku.

W odniesieniu do (2) zgadzam się, że pominięcie ważnych kwestii nie jest na pierwszy rzut oka znakiem przedwczesnej abstrakcji. Abstrakcja czy nie, możesz pominąć coś ważnego i jeśli nie zostanie znaleziona daleko w linii łańcucha abstrakcji, nie będzie trudniej ani łatwiej to rozwiązać.

Zamiast tego interpretuję to jako oznaczające, że każda abstrakcja stwarza ryzyko zaciemnienia modelu domeny. Niepoprawna abstrakcja może sprawić, że system będzie bardzo trudny do uzasadnienia, ponieważ tworzysz relacje, które mogą drastycznie wpłynąć na dalszy rozwój modelu domeny i zrozumienie domeny. Ryzykujesz pominięcie nieistotnych informacji na temat abstrakcji , ale całego systemu , biorąc swój model do niewłaściwej króliczej nory.

JimJam
źródło
„To brzmi trochę tak, jakbyś chciał rozwiązać problem, który jeszcze nie istnieje, i że nie masz uzasadnionego powodu, aby przypuszczać, że wystąpi, chyba że po prostu uważasz, że klient nie ma racji co do żądania”. - To wcale nie jest to, co powiedziałem, w rzeczywistości wyjaśniłem, że budowałem tylko na podstawie słów, których użył klient. To, że nie chciałem tego robić, skłoniło mnie do korzystania z abstrakcji.
James
„Poleciłbym więcej komunikacji niż wdrożenie rozwiązania zgadywania” - Masz na myśli, jak zasugerowałem? Cytując to, co powiedziałem w OP: „rozwiązaniem tego nie jest wymyślenie własnej konkrecji i marnowanie zasobów, kiedy odkryjesz, że pomyliłeś się, to uzyskać więcej informacji od klienta”.
James
„Choć może wydawać się nieszkodliwe po prostu uderzenie„ streszczenia ”w definicję klasy i czuć się bezpiecznie wiedząc, że możesz ją przedłużyć” - Kiedy mówię „abstrakcja” mam na myśli „abstrakcję”, nie mam na myśli „abstrakcyjnego słowa kluczowego”. Interfejsy mogą być abstrakcjami, abstrakcje są logiką rozmytą, o tym cały ten post mówi. Klient komunikuje się w kategoriach abstrakcji, więc powinniśmy programować w kategoriach abstrakcyjnych, dopóki nie dowiemy się więcej o domenie.
James
1
„może się okazać, że nigdy nie musisz” - To nie ma sensu. Klient mówi „Chcę robota, który pakuje przedmioty”, tworzysz abstrakcję spełniającą te kryteria. Wszelkie dalsze informacje, które otrzymujesz od klienta, będą szczegółowymi informacjami o tym, w jaki sposób chce się przeprowadzić pakowanie. Nie unieważnia to twojej abstrakcji, klient nie zmienia nagle zdania na temat podstawowej idei tego, co kupuje. Robot nadal będzie pakował przedmioty. Utworzona abstrakcja nadal będzie obowiązywać.
James
„lub możesz rozszerzyć go w sposób, który nadmiernie komplikuje projekt, abstrakcyjnie niewłaściwe rzeczy.”… Czy uczciwie przeczytałeś to, co powiedziałem, czy tylko tytuł? Poważnie? Przeczytaj punkty (1) i (2)
James