Skąd wiemy, że preferowanie kompozycji zamiast uogólnienia jest zawsze właściwym wyborem?

9

Niezależnie od tego, czy obiekt istnieje fizycznie, czy nie, możemy modelować go na różne sposoby. W wielu przypadkach moglibyśmy arbitralnie zastosować uogólnienie lub kompozycję. Jednak zasada GoF „faworyzuj kompozycję zamiast uogólnienia [sic]” prowadzi nas do użycia kompozycji. Kiedy modelujemy na przykład linię, tworzymy klasę zawierającą dwa elementy PointA i PointB typu Point (kompozycja) zamiast rozszerzania Point (uogólnienie). Jest to tylko uproszczony przykład tego, w jaki sposób możemy arbitralnie wybrać kompozycję lub dziedziczenie do modelu, mimo że obiekty są zwykle znacznie bardziej złożone.

Skąd wiemy, że jest to właściwy wybór? Ma to znaczenie przynajmniej dlatego, że może być mnóstwo refaktoryzacji, jeśli jest źle?

CarneyCode
źródło
9
Twój przykład tak naprawdę nie działa, ponieważ nie można powiedzieć, że linia jest punktem, a zatem nie spełnia zasady podstawienia Liskowa, a dziedziczenie nie jest właściwe.
Dean Harding,
@Dean: Jak zapewne wiesz, przykład nie jest najważniejszy. Dla zapisu linię można przedstawić jako równanie zdefiniowane przez dwa punkty.
CarneyCode
1
@Songo: Wygląda na to, że całkowicie przegapiłeś ten punkt.
CarneyCode
Czy rozszerzenie specjalizacji nie jest uogólnieniem?
Tulains Córdova

Odpowiedzi:

17

Nie zawsze jest to właściwy wybór. W większości przypadków jest to korzystne . Gdy model złożony wymaga zmiany lub rozszerzenia, jest znacznie bardziej odporny na to, ponieważ można zmienić skład bez obawy, że wpłynie on na inne klasy przypadkowo.

Skąd to wiemy? Z doświadczenia i doświadczenia innych.

Widziałem wiele sytuacji, w których ogromna hierarchia klas wyrosła z tego, co zaczęło się jako prosta koncepcja, i tam właśnie konieczne staje się twoje poważne refaktoryzowanie. Tymczasem nigdy nie widziałem sytuacji, w której struktura kompozycji wymagałaby refaktoryzacji do dziedziczenia.

Ale moje anegdotyczne dowody nie powinny wystarczyć. Wystarczy rozejrzeć się po Internecie, a spora liczba osób miała takie same doświadczenia.

pdr
źródło
Podoba mi się; weź głosowanie.
CarneyCode
+1 za „Nigdy nie widziałem sytuacji, w której struktura kompozycji wymagałaby refaktoryzacji do dziedziczenia”.
Kazark,
7

Jeśli chcesz mieć silniejszą ogólną zasadę wykraczającą poza „faworyzowanie kompozycji zamiast dziedziczenia”, mógłbym zaproponować coś takiego:

Z dwóch sposobów specjalizacji obiektu - Dziedziczenie i Kompozycja - należy używać dziedziczenia tylko wtedy, gdy potrzebujesz, aby Twój obiekt był polimorficzny (zastępowalny) dla klasy podstawowej, którą specjalizujesz.

Jednak jak wszystkie praktyczne zasady, kiedy zrozumiesz regułę - wtedy możesz ją złamać :)

Stephen Bailey
źródło
0

Nie rozumiem, w jaki sposób kompozycja i uogólnienie są alternatywami i nie udało mi się znaleźć cytatu .
Skład i dziedziczenie są (w celu osiągnięcia specjalizacji) i można argumentować, że abstrakcja i uogólnienie są (w celu osiągnięcia modułowości).

To, czego chcesz, to aby Twój projekt był prosty i wiarygodny, a są to dwie cechy, które z natury dość łatwo można zmierzyć.
W twoim przypadku bardziej prawdopodobne wydaje się skomponowanie linii dwóch punktów zamiast jej przedłużenia, ponieważ naturalnie zdefiniowałbyś linię o dwa punkty, zamiast rozszerzając koncepcję punktu.

back2dos
źródło
Czy to jest bardziej naturalne? Linia nie ma więcej niż dwóch punktów niż pojedynczy punkt przedłużony?
CarneyCode
2
Nigdy nie widziałem definicji linii, która nie uwzględnia faktu, że składa się ona z ponad 2 punktów. Nawet używając równania linii, jeśli masz w domenie liczb tylko 1 x wartość, jest to punkt, a nie linia.
Przypon
0

Przysługa zdarza się, gdy obaj kandydaci się kwalifikują. Problem wskazany w pytaniu zawodzi na tym koncie, ponieważ ma on tylko opcję składu.

„Kompozycja mogłaby również użyć uogólnienia”. Czy to stwierdzenie ma dla nas sens? Jeśli nie, to nie jesteśmy jeszcze gotowi na przyjęcie zasady „przychylności”.

Dlatego preferujemy Composition, ponieważ Composition oferuje więcej możliwości rozszerzenia / elastyczności niż generalizacja. To rozszerzenie / elastyczność dotyczy głównie środowiska wykonawczego / elastyczności dynamicznej (co osiąga się dzięki połączeniu interfejsów i kompozycji).

Korzyści nie są natychmiast widoczne. Aby zobaczyć korzyści, musisz poczekać na kolejne nieoczekiwane żądanie zmiany. Tak więc w większości przypadków ci, którzy trzymali się uogólnienia, zawodzą w porównaniu z tymi, którzy przyjęli kompozycję (z wyjątkiem jednego oczywistego przypadku wspomnianego później). Stąd zasada. Z naukowego punktu widzenia, jeśli możesz skutecznie wdrożyć zastrzyk zależności, powinieneś wiedzieć, który z nich preferować i kiedy. Reguła pomaga w podjęciu decyzji, gdy nie jesteś pewien, który wybrać. Ponownie powinieneś być w stanie zobaczyć obie opcje w pierwszej kolejności, aby faworyzować jedną z nich.

Podsumowanie: Kompozycja: Sprzęganie jest redukowane przez tylko kilka mniejszych rzeczy, które podłączasz do czegoś większego, a większy obiekt po prostu wywołuje z powrotem mniejszy obiekt. Generalizacja: Z punktu widzenia interfejsu API innej firmy zdefiniowanie, że metoda może zostać zastąpiona, jest silniejszym zobowiązaniem niż zdefiniowanie, że można wywołać metodę (pewnie wygrana w przypadku Generalizacji). I nigdy nie zapominaj, że w kompozycji używasz również uogólnienia, z interfejsu zamiast dużej klasy. Niestety cały kredyt należy do składu.

Blue Clouds
źródło
-4

Dzisiaj napisałem o 300 LoC. I nie pamiętam żadnej zasady, którą mógłbym naruszać i której nie naruszałem. Mam nadzieję, że refaktoryzacja uratuje moją duszę.

Jeśli abstrakcja jest podyktowana przez interfejs strony 3D - zwykle poprawne jest użycie dziedziczenia. W krajowym projekcie podmiot musi być abstrakcyjny lub zapieczętowany. Podmiot abstrakcyjny potencjalnie musi mieć około 3 spadkobierców. Nie lubię punktów rozszerzeń z jedną metodą wirtualną. Wszystko powyżej dotyczy mojego smaku. Nie mówię o abstrakcji interfejsu, która jest kompletną inną historią.

Andrew_B
źródło