Woher wissen wir, dass es immer die richtige Wahl ist, Komposition gegenüber Generalisierung zu bevorzugen?

9

Unabhängig davon, ob ein Objekt physisch existiert oder nicht, können wir es auf verschiedene Arten modellieren. In vielen Fällen könnten wir willkürlich Verallgemeinerung oder Komposition verwenden. Das GoF-Prinzip "Komposition gegenüber Verallgemeinerung bevorzugen" führt uns jedoch zur Verwendung von Komposition. Wenn wir beispielsweise eine Linie modellieren, erstellen wir eine Klasse, die zwei Elemente PointA und PointB vom Typ Point (Zusammensetzung) enthält, anstatt Point (Generalisierung) zu erweitern. Dies ist nur ein vereinfachtes Beispiel dafür, wie wir die Zusammensetzung oder Vererbung willkürlich für das Modell auswählen können, obwohl Objekte normalerweise viel komplexer sind.

Woher wissen wir, dass dies die richtige Wahl ist? Es ist zumindest wichtig, weil es eine Menge Refactoring geben könnte, wenn es falsch ist?

CarneyCode
quelle
9
Ihr Beispiel funktioniert nicht wirklich, weil Sie nicht sagen können, dass eine Linie ein Punkt ist, und daher das Liskov-Substitutionsprinzip nicht erfüllt und die Vererbung nicht angemessen ist.
Dean Harding
@ Dean: Wie Sie wahrscheinlich wissen, ist das Beispiel nicht zentral für den Punkt. Für den Datensatz kann eine Linie als eine durch zwei Punkte definierte Gleichung dargestellt werden.
CarneyCode
1
@ Songo: Es scheint, dass Sie den Punkt völlig verpasst haben.
CarneyCode
Erweitert man nicht die Spezialisierung statt die Generalisierung?
Tulains Córdova

Antworten:

17

Es ist nicht immer die richtige Wahl. In den meisten Fällen ist es das günstigste . Wenn ein zusammengesetztes Modell geändert oder erweitert werden muss, ist es wesentlich widerstandsfähiger, da Sie die Zusammensetzung ändern können, ohne befürchten zu müssen, dass andere Klassen versehentlich beeinflusst werden.

Woher wissen wir das? Aus Erfahrung und der Erfahrung anderer.

Ich habe viele Situationen gesehen, in denen eine massive Klassenhierarchie aus einem einfachen Konzept heraus gewachsen ist, und hier wird Ihr umfangreiches Refactoring notwendig. In der Zwischenzeit habe ich noch nie eine Situation gesehen, in der eine Kompositionsstruktur in Vererbung umgestaltet werden musste.

Aber meine anekdotischen Beweise sollten nicht ausreichen. Schauen Sie sich einfach im Internet um und viele Leute haben die gleichen Erfahrungen gemacht.

pdr
quelle
Ich mag es; nimm eine Gegenstimme.
CarneyCode
+1 für "Ich habe noch nie eine Situation gesehen, in der eine Kompositionsstruktur in Vererbung umgestaltet werden musste."
Kazark
7

Wenn Sie eine stärkere Faustregel wünschen, die über "Komposition gegenüber Vererbung bevorzugen" hinausgeht, dann könnte ich Folgendes vorschlagen:

Von den beiden Möglichkeiten, ein Objekt zu spezialisieren - Vererbung und Zusammensetzung - sollten Sie die Vererbung nur verwenden, wenn Ihr Objekt für die Basisklasse, auf die Sie sich spezialisieren, polymorph (ersetzbar) sein muss.

Wie bei allen Faustregeln können Sie jedoch die Regel brechen, sobald Sie die Regel verstanden haben :)

Stephen Bailey
quelle
0

Ich sehe nicht, wie Komposition und Verallgemeinerung Alternativen sind, und habe das Zitat nicht gefunden .
Zusammensetzung und Vererbung sind (um Spezialisierung zu erreichen), und es kann argumentiert werden, dass Abstraktion und Generalisierung sind (um Modularität zu erreichen).

Was Sie wollen, ist, dass Ihr Design einfach und plausibel ist. Dies sind zwei Eigenschaften, die von Natur aus recht einfach zu messen sind.
In Ihrem Fall erscheint es plausibler, eine Linie aus zwei Punkten zu erstellen, anstatt sie zu erweitern, da Sie eine Linie natürlich um zwei Punkte definieren würden, anstatt das Konzept eines Punkts zu erweitern.

back2dos
quelle
Ist es natürlicher? Eine Linie besteht nicht aus mehr als zwei Punkten als ein einzelner Punkt?
CarneyCode
2
Ich habe noch nie eine Liniendefinition gesehen, die nicht die Tatsache beinhaltet, dass sie aus 2+ Punkten besteht. Selbst wenn Sie eine Liniengleichung verwenden, wenn Sie nur 1 x Wert in Ihrer Zahlendomäne haben, ist dies ein Punkt, keine Linie.
Rig
0

Gunst entsteht, wenn sich beide Kandidaten qualifizieren. Das in der Frage angegebene Problem schlägt in diesem Konto fehl, da nur eine Kompositionsoption vorhanden ist.

"Komposition könnte auch Verallgemeinerung gebrauchen". Ist diese Aussage für uns sinnvoll? Wenn nicht, sind wir noch nicht bereit, die Gunstregel zu verstehen.

Wir bevorzugen Komposition, weil Komposition mehr Erweiterungs- / Flexibilitätsmöglichkeiten bietet als Generalisierung. Diese Erweiterung / Flexibilität bezieht sich hauptsächlich auf die Laufzeit / dynamische Flexibilität (die durch die Kombination von Schnittstellen und Zusammensetzung erreicht wird).

Der Nutzen ist nicht sofort sichtbar. Um den Nutzen zu sehen, müssen Sie auf die nächste unerwartete Änderungsanforderung warten. In den meisten Fällen scheitern diejenigen, die an der Verallgemeinerung festhalten, im Vergleich zu denen, die sich der Komposition verschrieben haben (mit Ausnahme eines offensichtlichen Falls, der später erwähnt wird). Daher die Regel. Wenn Sie aus Lernsicht eine Abhängigkeitsinjektion erfolgreich implementieren können, sollten Sie wissen, welche Sie wann bevorzugen sollten. Die Regel hilft Ihnen bei der Entscheidung, wenn Sie sich nicht sicher sind, welche Sie wählen sollen. Auch hier sollten Sie in der Lage sein, beide Optionen zu sehen, um eine zu bevorzugen.

Zusammenfassung: Zusammensetzung: Die Kopplung wird reduziert, indem nur einige kleinere Dinge an etwas Größeres angeschlossen werden, und das größere Objekt ruft nur das kleinere Objekt zurück. Generierung: Aus Sicht der API eines Drittanbieters ist die Definition, dass eine Methode überschrieben werden kann, eine stärkere Verpflichtung als die Definition, dass eine Methode aufgerufen werden kann (sicherer Gewinn für die Generalisierung). Und vergessen Sie niemals, dass Sie bei Kompositionen auch Generalisierung verwenden, und zwar über eine Schnittstelle anstelle einer großen Klasse. Aber der ganze Kredit geht leider an die Komposition.

Blue Clouds
quelle
-4

Heute habe ich ungefähr 300 LoC geschrieben. Und ich kann mich an keinen Grundsatz erinnern, gegen den ich verstoßen konnte und den ich nicht verletzt habe. Refactoring wird meine Seele retten, hoffe ich.

Wenn die Abstraktion von der 3D-Party-API diktiert wird, ist es normalerweise richtig, die Vererbung zu verwenden. Im häuslichen Design muss die Entität entweder abstrakt oder versiegelt sein. Abstrakte Entität muss möglicherweise ungefähr 3 Erben haben. Ich mag keine Erweiterungspunkte für eine virtuelle Methode. Alles oben dreht sich um meinen Geschmack. Ich spreche nicht von Schnittstellenabstraktion, was eine völlig andere Geschichte ist.

Andrew_B
quelle