Frage

Es gibt zwei Denkschulen, wie man am besten erweitern, verbessern und die Wiederverwendung von code in einer Objekt-orientierten system:

  1. Vererbung:erweitern Sie die Funktionalität einer Klasse durch erstellen einer Unterklasse.Override Oberklasse-Mitglieder in den Unterklassen neue Funktionen bereitstellen.Machen Sie Methoden abstrakte/virtuelle Kraft, der Unterklassen auf "fill-in-the-blanks", wenn die Oberklasse will ein bestimmtes interface aber ist agnostisch, was für Ihre Umsetzung.

  2. Aggregation:erstellen Sie neue Funktionen durch andere Klassen, und kombinieren Sie Sie in eine neue Klasse.Befestigen Sie eine gemeinsame Schnittstelle, um diese neue Klasse für die Interoperabilität mit anderen code.

Was sind die Vorteile, die Kosten und die Folgen?Gibt es andere alternativen?

Ich sehe diese Debatte auf einer regelmäßigen basis, aber ich glaube nicht, dass er gebeten wurde, auf Stack Overflow noch (obwohl es einige zugehörige Diskussion).Es gibt auch einen überraschenden Mangel an guten Google-Ergebnisse für Sie.

War es hilfreich?

Lösung

Es ist nicht eine Frage von dem ist die beste, aber wenn das, was zu verwenden.

In den ‚normalen‘ Fällen eine einfache Frage ist genug, um herauszufinden, ob wir Vererbung oder Aggregation benötigen.

  • Wenn die neue Klasse ist mehr oder weniger als die ursprüngliche Klasse. Verwenden Sie Vererbung. Die neue Klasse ist jetzt eine Unterklasse der ursprünglichen Klasse.
  • Wenn die neue Klasse muss Haben die ursprüngliche Klasse. Verwenden Sie Aggregation. Die neue Klasse hat jetzt die ursprüngliche Klasse als Mitglied.

Allerdings gibt es eine große Grauzone. Also brauchen wir einige andere Tricks.

  • Wenn wir Vererbung verwendet (oder wir planen, es zu benutzen), aber wir Teil der Schnittstelle nur benutzen, oder sind wir gezwungen, eine Vielzahl von Funktionen außer Kraft zu setzen, die die Korrelation logisch zu halten. Dann haben wir einen großen unangenehmen Geruch ab, der angibt, dass wir Aggregation verwenden musste.
  • Wenn wir Aggregation verwendet haben (oder wir planen, es zu benutzen), aber wir herausfinden, müssen wir fast die gesamte Funktionalität kopieren. Dann haben wir einen Geruch, der in Richtung der Vererbung zeigt.

Um es kurz zu schneiden. Wir sollten Aggregation verwenden, wenn ein Teil der Schnittstelle nicht verwendet wird oder geändert werden, um eine unlogische Situation zu vermeiden. Wir brauchen nur Vererbung zu verwenden, wenn wir fast alle Funktionen ohne größere Änderungen benötigen. Und im Zweifelsfall verwendet Aggregation.

Eine andere Möglichkeit, der Fall, dass wir eine Klasse, die ein Teil der Funktionalität der ursprünglichen Klasse braucht, ist die ursprüngliche Klasse in einer Wurzelklasse und Unterklasse zu spalten. Und lassen Sie die neue Klasse erbt von der Wurzelklasse. Aber Sie sollten sich mit darum kümmern, nicht eine unlogische Trennung zu schaffen.

wir ein Beispiel hinzuzufügen. Wir haben eine Klasse 'Dog' mit Methoden: 'essen', 'Walk', 'Bark', 'Play'.

class Dog
  Eat;
  Walk;
  Bark;
  Play;
end;

Wir brauchen jetzt eine Klasse ‚Katze‘, die ‚essen‘, ‚Weg‘, ‚Purr‘ und ‚Play‘ muss. So wird zuerst versuchen, es von einem Hund zu verlängern.

class Cat is Dog
  Purr; 
end;

Sieht aus, in Ordnung, aber warten. Diese Katze kann bellen (Katzenliebhaber töten mich dafür). Und ein Bellen Katze gegen die Prinzipien des Universums. Also müssen wir die Bark-Methode überschrieben, so dass er nichts tut.

class Cat is Dog
  Purr; 
  Bark = null;
end;

Ok, das funktioniert, aber es riecht schlecht. So kann eine Aggregation versuchen:

class Cat
  has Dog;
  Eat = Dog.Eat;
  Walk = Dog.Walk;
  Play = Dog.Play;
  Purr;
end;

Ok, das ist schön. Diese Katze bellen nicht mehr, nicht einmal still. Aber noch hat es einen internen Hund, der will aus. So können versuchen Lösung Nummer drei:

class Pet
  Eat;
  Walk;
  Play;
end;

class Dog is Pet
  Bark;
end;

class Cat is Pet
  Purr;
end;

Das ist viel sauberer. Keine internen Hunde. Und Katzen und Hunde sind auf dem gleichen Niveau. Wir können auch andere Haustiere vorstellen, das Modell zu erweitern. Es sei denn, es ist ein Fisch, oder etwas, das geht nicht. In diesem Fall müssen wir wieder Refactoring. Aber das ist etwas für eine andere Zeit.

Andere Tipps

Zu Beginn des GOF sie angeben

  

Favor Objekt Zusammensetzung über Klassenvererbung.

Dies wird weiter diskutiert hier

Der Unterschied typischerweise als die Differenz zwischen ausgedrückt wird „ein“ und „hat einen“. Vererbung, die "ist eine" -Beziehung, ist gut in der Liskov Substitution Principle . Aggregation, die „hat eine“ Beziehung, ist genau das - es zeigt, dass das Ansammeln Objekt hat eine der aggregierten Objekte

.

Weitere Unterschiede gibt es auch -. Private Vererbung in C ++ zeigt eine Beziehung „in Bezug auf die umgesetzt wird“, was auch durch die Aggregation von (nicht belichteten) modelliert werden kann Mitglied als auch Objekte

Hier ist mein häufigste Argument:

In jedem objektorientierten System gibt es zwei Teile zu jeder Klasse:

  1. Das Schnittstelle : Das "öffentliches Gesicht" des Objekts. Dies ist der Satz von Fähigkeiten es den Rest der Welt verkündet. In vielen Sprachen wird der Satz auch in eine „Klasse“ definiert. Normalerweise sind die Methodensignaturen des Objekts, obwohl es ein wenig nach Sprache variiert.

  2. Die Implementierung : die „hinter den Kulissen“ Arbeit, die das Objekt tut, um seine Schnittstelle zu erfüllen und Funktionalität. Dies ist in der Regel der Codes und Mitgliedsdaten des Objekts.

Eines der Grundprinzipien der OOP ist, dass die Umsetzung ist verkapselt (dh versteckt) in der Klasse; das einzige, was Außenstehende ist die Schnittstelle sehen soll.

Wenn eine Unterklasse von einer Unterklasse erbt, erbt die Regel beide die Implementierung und die Schnittstelle. Dies wiederum bedeutet, dass Sie gezwungen sowohl als Einschränkungen für Ihre Klasse zu akzeptieren.

Mit Aggregation, erhalten Sie entweder Implementierung oder Schnittstelle oder beides wählen - aber du bist nicht in entweder gezwungen. Die Funktionalität eines Objekts wird zu dem Objekt selbst überlassen. Es kann auf andere Objekte verschieben, wie es mag, aber es ist letztlich für sich selbst verantwortlich. Meiner Erfahrung nach führt dies zu einem flexibleren System. Eines, das einfacher zu ändern

Also, wenn ich objektorientierte Software-Entwicklung bin, ziehe ich es fast immer Aggregation über Vererbung.

Ich habe eine Antwort auf „Ist ein“ vs " hat ein“: welche besser ist

?.

Im Grunde bin ich mit anderen Leuten: Verwendung Vererbung nur, wenn die abgeleitete Klasse wirklich ist der Typ Sie erstreckt, nicht nur, weil es enthält die gleichen Daten. Denken Sie daran, dass Vererbung bedeutet, dass die Unterklasse die Methoden gewinnt, sowie die Daten.

Ist es sinnvoll für Ihre abgeleitete Klasse machen alle haben die Methoden der Oberklasse? Oder versprechen Sie nur sich selbst leise, dass diese Methoden sollten in der abgeleiteten Klasse ignoriert werden? Oder finden Sie sich Methoden aus der übergeordneten Klasse überschreiben, so dass sie keine-ops machen, damit niemand sie versehentlich ruft? Oder Hinweise auf Ihr API doc-Generierungs-Tool gibt die Methode aus dem Dokument zu verzichten?

Das sind starke Hinweise darauf, dass die Aggregation die bessere Wahl in diesem Fall ist.

Ich sehe eine Menge „ist-a vs. hat-ein, sie konzeptionell anders sind“ Antworten auf diese und die damit verbundenen Fragen.

Das einzige, was ich in meiner Erfahrung gefunden habe, ist, dass, ob eine Beziehung zu bestimmen versucht wird „ist-a“ oder „hat-ein“ ist zum Scheitern verurteilt. Auch wenn Sie jetzt richtig, dass die Bestimmung für die Objekte machen können, bedeuten ändernden Anforderungen, dass Sie wahrscheinlich irgendwann in der Zukunft falsch sein werden.

Eine andere Sache, die ich gefunden habe ist, dass es sehr hart von der Erbschaftssteuer zu konvertieren einmal zur Aggregation eine Menge Code gibt es um eine Vererbungshierarchie geschrieben. Ist gerade von einer übergeordneten Klasse Wechsel zu einem Schnittstellenmittel in dem System nahezu jede Unterklasse zu ändern.

Und, wie ich an anderer Stelle in diesem Beitrag erwähnt, Aggregation neigt als Erbteil weniger flexibel zu sein.

So haben Sie einen perfekten Sturm von Argumenten gegen Vererbung, wenn Sie das eine oder andere wählen:

  1. Ihre Wahl wird wahrscheinlich die falsche irgendwann sein
  2. , diese Wahl zu ändern ist schwierig, wenn man es geschafft hat.
  3. Vererbung neigt eine schlechtere Wahl zu sein, da es mehr beschränkende ist.

So neige ich dazu, eine Aggregation zu wählen - auch wenn es ein starkes ist-ein-Beziehung zu sein scheint

.

Die Frage wird in der Regel als Zusammensetzung vs. Vererbung ausdrückte, und es wurde hier gefragt vor.

Das wollte ich ein Kommentar auf die ursprüngliche Frage machen, aber 300 Zeichen Bisse [;. <)

Ich denke, wir müssen vorsichtig sein. Erstens gibt es mehr Aromen als die beiden eher konkrete Beispiele in der Frage gemacht.

Auch ich schlage vor, dass es wertvoll ist nicht das Ziel, mit dem Instrument zu verwirren. Man will sicherstellen, dass die gewählte Technik oder Methodik Erreichung des vorrangigen Ziels unterstützt, aber ich tut, was nicht out-of-context which-Technik-is-beste Diskussion sehr nützlich ist. Es hilft die Gefahren der verschiedenen Ansätze zusammen mit ihren klaren Sweet-Spots zu kennen.

Zum Beispiel, was sind Sie aus zu erreichen, was Sie zur Verfügung haben, mit zu beginnen, und was sind die Einschränkungen?

Erstellen Sie ein Komponenten-Framework, auch eine besondere einen Zweck? Sind Schnittstellen von Implementierungen im Programmiersystem trennbare oder sie durch eine Praxis erreicht wird, eine andere Art von Technologie? Können Sie die Vererbungsstruktur von Schnittstellen (falls vorhanden) aus der Vererbungsstruktur der Klassen trennen, die sie umsetzen? Ist es wichtig, die Klassenstruktur einer Implementierung aus dem Code zu verbergen, die die Implementierung liefert an den Schnittstellen beruht? Gibt es mehrere Implementierungen verwendbar sein zur gleichen Zeit oder die Variation mehr über Zeit als Folge der Wartung und enhancememt? Diese und weitere Bedürfnisse in Betracht gezogen werden, bevor Sie auf einem Werkzeug oder eine Methode fixieren.

Schließlich ist es, dass wichtige Unterschiede in der Abstraktion zu sperren und wie halten Sie davon (wie in IS-A gegen has-a) auf verschiedene Merkmale der OO-Technologie? Vielleicht so, hält, wenn es die konzeptionelle Struktur konsistent und überschaubar für Sie und andere. Aber es ist nicht klug, damit versklavt werden und die Verrenkungen Sie könnten am Ende macht. Vielleicht ist es am besten, eine Ebene zurückstehen und nicht so starr sein (aber gute Erzählung zu lassen, damit andere sagen, was los ist). [Ich suche, was macht einen bestimmte Teil eines Programm erklärbar, aber einige Male Ich gehe für Eleganz, wenn es ein größerer Gewinn ist. Nicht immer die beste Idee.]

Ich bin ein Schnittstelle Purist, und ich bin auf die Arten von Problemen gezogen und Ansätze, bei denen Schnittstelle Purismus geeignet ist, ob ein Java-Framework bauen oder einige COM-Implementierungen zu organisieren. Das macht es nicht angemessen für alles, nicht einmal in der Nähe alles, obwohl ich darauf schwören. (Ich habe ein paar Projekte, die ernsthaften Gegenbeispiele gegen Schnittstelle Purismus zu bieten scheinen, so wird es interessant sein zu sehen, wie ich es schaffe fertig zu werden.)

Ich denke, es ist kein entweder / oder Debatte. Es ist nur so, dass:

  1. ist-a (Vererbung) Beziehungen treten weniger häufig als hat-eine (Komposition) Beziehungen.
  2. Die Vererbung ist schwieriger zu Recht zu bekommen, auch wenn es angebracht ist, es zu benutzen, so Sorgfalt getroffen werden muss, weil es Verkapselung brechen kann, fördert eine enge Kopplung durch Umsetzung auszusetzen und so weiter.

Beide haben ihren Platz, aber Vererbung ist riskanter.

Obwohl natürlich wäre es nicht sinnvoll, eine Klasse-Form haben ‚mit-eine‘ Point und ein Quadrat Klasse. Hier Vererbung zurückzuführen ist.

Menschen über die Vererbung neigen dazu glauben zuerst, wenn sie versuchen, etwas dehnbar zu entwerfen, das ist, was falsch ist.

Ich werde die where-diese-könnte-Anwendung abdecken Teil. Hier ist ein Beispiel von beiden, in einem Spiel-Szenario. Nehmen wir an, es gibt ein Spiel, das verschiedene Arten von Soldaten hat. Jeder Soldat kann einen Ranzen hat, die verschiedene Dinge halten kann.

Vererbung hier? Es ist ein Marine, grünes Barett und ein Scharfschütze. Dies sind Arten von Soldaten. So gibt es eine Basisklasse Soldaten mit Meeres-, Green Beret & Sniper als abgeleitete Klassen

Aggregation hier? Der Ranzen kann enthalten Granaten, Pistolen (verschiedene Typen), Messer, Medikit usw. Ein Soldat kann mit jedem von ihnen zu einem bestimmten Zeitpunkt ausgestattet sein, und er kann auch eine kugelsichere Weste hat, die als Rüstung wirkt, wenn sie angegriffen und seine Verletzungen verringert sich auf einen bestimmten Prozentsatz. Der Soldat Klasse enthält ein Objekt der Panzerweste Klasse und der Ranzen Klasse, die Verweise auf diese Elemente enthält.

Zugunsten passiert, wenn beide Kandidaten qualifiziert.A und B sind die Optionen, und Sie begünstigen A.Der Grund dafür ist, dass die Komposition bietet mehr extension/flexiblity Möglichkeiten als Verallgemeinerung.Diese Erweiterung/Flexibilität bezieht sich meist auf Laufzeit - /dynamische Flexibilität.

Der nutzen ist nicht sofort sichtbar.Um zu sehen die Vorteile, die Sie Notwendigkeit zu warten für die nächste unerwartete änderung Anfrage.Also in den meisten Fällen diejenigen, die stilistisch generlalization schlägt fehl, wenn im Vergleich zu denen, die den Zusammensetzung(außer einem offensichtlichen Fall später erwähnt).Daher die Regel.Von einem lernen, wenn Sie implementieren können, ist eine Abhängigkeit Injektion erfolgreich, dann sollten Sie wissen, welche Sie bevorzugen, und wenn.Die Regel hilft Ihnen bei der Entscheidung, wie gut;wenn Sie nicht sicher sind, dann wählen Sie die Komposition.

Zusammenfassung:Zusammensetzung :Die Kupplung wird reduziert, indem nur ein paar kleinere Dinge, die Sie plug in etwas größer, und die größeren Objekt ruft nur das kleinere Objekt zurück.Generlization:Aus API-Sicht zu definieren, dass eine Methode überschrieben werden, ist eine größere Verpflichtung als die Definition, dass eine Methode aufgerufen werden kann.(nur sehr wenige Gelegenheiten, wenn die Verallgemeinerung gewinnt).Und vergessen Sie nie, dass mit der Zusammensetzung sind Sie mit der Vererbung auch von einer Schnittstelle anstelle einer großen Klasse

werden beiden Ansätze verwendet, um verschiedene Probleme zu lösen. Man muss nicht immer müssen über zwei oder mehr Klassen aggregieren, wenn sie von einer Klasse erben.

Manchmal müssen Sie eine einzelne Klasse aggregieren, weil diese Klasse versiegelt ist oder anderweitig nicht-virtuelle Mitglieder, die Sie abfangen müssen so erstellen Sie eine Proxy-Schicht, die offensichtlich in Bezug auf der Vererbung nicht gültig ist, aber so lange, wie die Klasse Sie sind Proxying ist eine Schnittstelle, die Sie diese abonnieren können ziemlich gut funktioniert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top