2010-02-10 10 views
23

Zusammensetzung und Vererbung.Gibt es irgendetwas, das die Vererbung nicht erreichen kann?

Ich bin mir bewusst, dass sie beide Tools sind, wenn sie geeignet sind, und Kontext ist sehr wichtig bei der Wahl zwischen Zusammensetzung und Vererbung. Die Diskussion über den entsprechenden Kontext ist jedoch meist etwas unscharf; Das hat mich dazu gebracht, darüber nachzudenken, wie deutlich Vererbung und Polymorphismus getrennte Aspekte traditioneller OOPs sind.

Polymorphismus ermöglicht es, "is-a" Beziehungen gleichermaßen sowie Vererbung anzugeben. Insbesondere erzeugt das Erben von einer Basisklasse implizit eine polymorphe Beziehung zwischen dieser Klasse und ihren Unterklassen. Während Polymorphismus jedoch unter Verwendung reiner Schnittstellen implementiert werden kann, verkompliziert die Vererbung die polymorphe Beziehung durch gleichzeitiges Übertragen von Implementierungsdetails. Auf diese Weise unterscheidet sich die Vererbung deutlich vom reinen Polymorphismus.

Als Werkzeug dient Vererbung anders Programmierer als Polymorphismus (durch reine Schnittstellen) durch Umsetzung wiederverwendet in trivialen Fälle vereinfacht. In den meisten Fällen stehen die Implementierungsdetails einer Oberklasse jedoch in einem subtilen Konflikt mit den Anforderungen einer Unterklasse. Deshalb haben wir "overrides" und "member hiding". In diesen Fällen wird die Wiederverwendung der Implementierung, die durch die Vererbung angeboten wird, mit dem zusätzlichen Aufwand der Überprüfung von Statusänderungen und Ausführungspfaden über kaskadierende Codeebenen hinweg erworben: Die vollständigen "abgeflachten" Implementierungsdetails der Unterklasse sind auf mehrere Klassen verteilt, was normalerweise bedeutet mehrere Dateien, von denen nur Teile für die betreffende Unterklasse gelten. Das Durchsehen dieser Hierarchie ist im Umgang mit der Vererbung absolut notwendig, denn ohne den Code der Oberklasse zu betrachten, gibt es keine Möglichkeit zu wissen, welche unbeherrschten Details mit Ihrem Zustand zusammenhängen oder Ihre Ausführung umleiten.

Im Vergleich, exklusive Verwendung von Komposition garantiert, dass Sie sehen können, welcher Status durch explizit instanziierte Objekte geändert werden kann, deren Methoden nach Ihrem Ermessen aufgerufen werden. Eine wirklich abgeflachte Implementierung wird immer noch nicht erreicht (und ist tatsächlich nicht einmal wünschenswert, da der Nutzen von strukturierter Programmierung die Kapselung und Abstraktion von Implementierungsdetails ist), aber Sie erhalten trotzdem Ihre Code-Wiederverwendung und Sie müssen nur an einer Stelle suchen wenn der Code sich falsch verhält.

Mit dem Ziel, diese Ideen in der Praxis zu testen, traditionelles Erbe für eine Kombination aus reinem Interface-basierten Polymorphismus und Objekt Zusammensetzung meidet, ich frage mich,

Gibt es Objekt Zusammensetzung etwas und Schnittstellen kann das Erbe nicht erreichen können ?

bearbeiten

In den Antworten bisher ewernli glaubt, dass es keine technischen Meisterleistungen für eine Technik verfügbar sind, aber nicht die andere; Er erwähnt später, wie verschiedene Muster und Designansätze für jede Technik inhärent sind. Dies liegt nahe. Der Vorschlag führt mich jedoch dazu, meine Frage zu präzisieren, indem ich frage, ob die ausschließliche Verwendung von Komposition und Schnittstellen anstelle der traditionellen Vererbung die Verwendung von wichtigen Entwurfsmustern verbieten würde. Und wenn ja, gibt es nicht gleichwertige Muster für meine Situation?

+2

Ich persönlich mag Mixins. :) –

+1

Ich habe keine Zeit für eine effektive Duplizierung zu überprüfen, aber diese Vererbung v. Zusammensetzung Thema wird ständig auf SO besucht, für ex. http://stackoverflow.com/questions/216523/object-oriented-best-practices-inheritance-v-composition-v-interfaces oder http://stackoverflow.com/questions/1598722/should-i-use-inheritance -oder-Zusammensetzung. "Man kann diesem Thema immer eine Wendung geben und es als eine neue Sicht auf die Dinge bezeichnen ..." Eine Sache, der wir zustimmen sollten, ist, dass diese Art von Frage nicht zu endgültigen oder sogar autoritativen Antworten führt. Vielleicht ist ein CW-Format besser geeignet ... – mjv

+0

Ich wollte nicht eine müde Debatte wieder aufleben lassen. Zugegebenermaßen war die Art und Weise, wie ich meinen Fall darlegte, eher eine einseitige Probe dieser Debatte, aber mein Hauptinteresse liegt in der Beantwortung der Frage, ob es eine Vererbungsform gibt, die wirklich nicht durch Komposition und Schnittstellen ersetzt werden kann. p.s., was ist ein CW-Format? Vielleicht werde ich das versuchen ... –

Antwort

21

Technisch kann alles, was mit Vererbung realisiert werden kann, auch mit Delegation realisiert werden. Die Antwort wäre also "Nein".

Transforming Erbschaft in Delegation

Lassen Sie uns sagen, dass wir die folgenden Klassen mit Vererbung implementiert haben:

public class A { 
    String a = "A"; 
    void doSomething() { .... } 
    void getDisplayName() { return a } 
    void printName { System.out.println(this.getDisplayName() }; 
} 

public class B extends A { 
    String b = "B"; 
    void getDisplayName() { return a + " " + b; } 
    void doSomething() { super.doSomething() ; ... }  
} 

Das Zeug funktioniert gut, und auf einer Instanz von B printName Aufruf wird "A B" in den Druck Konsole.

Wenn wir nun umschreiben, dass mit Delegation, erhalten wir:

public class A { 
    String a = "A"; 
    void doSomething() { .... } 
    void getDisplayName() { return a } 
    void printName { System.out.println(this.getName() }; 
} 

public class B { 
    String b = "B"; 
    A delegate = new A(); 
    void getDisplayName() { return delegate.a + " " + b; } 
    void doSomething() { delegate.doSomething() ; ... } 
    void printName() { delegate.printName() ; ... } 
} 

Wir brauchen printName in B zu definieren und auch die Delegierten zu schaffen, wenn B instanziiert wird. Ein Aufruf an doSomething funktioniert ähnlich wie bei der Vererbung. Aber ein Anruf an printName wird "A" in der Konsole drucken. Tatsächlich haben wir mit der Delegierung das mächtige Konzept verloren, dass "dieses" an die Objektinstanz gebunden ist und die Basismethoden Methoden aufrufen können, die überschrieben werden müssen.

Dies kann in der Sprache gelöst werden unterstützt reine Delegation. Bei reiner Delegierung verweist "this" im Delegaten immer noch auf die Instanz von B. Das bedeutet, dass this.getName() den Methodenversand aus Klasse B startet. Das erreichen wir genauso wie bei der Vererbung. Dies ist der Mechanismus in prototype-based Sprache wie Self, die Delegierung hat eine integrierte Funktion (Sie können lesen here, wie die Vererbung in Self funktioniert).

Aber Java hat keine reine Delegierung. Bist du dann fest? Nein wirklich, können wir noch tun, dass sie mit etwas mehr Aufwand:

public class A implements AInterface { 
    String a = "A"; 
    AInterface owner; // replace "this" 
    A (AInterface o) { owner = o } 
    void doSomething() { .... } 
    void getDisplayName() { return a } 
    void printName { System.out.println(owner.getName() }; 
} 

public class B implements AInterface { 
    String b = "B"; 
    A delegate = new A(this); 
    void getDisplayName() { return delegate.a + " " + b; } 
    void doSomething() { delegate.doSomething() ; ... } 
    void printName() { delegate.printName() ; ... } 
} 

Wir sind im Grunde Neuimplementierung, was die Einbau-Vererbung zur Verfügung stellt. Macht das Sinn? Nicht wirklich. Aber es veranschaulicht, dass Vererbung immer in Delegierung konvertiert werden kann.

Discussion

Inheritance ist dadurch gekennzeichnet, dass eine Basisklasse eine Methode aufrufen können, die in einer Unterklasse überschrieben wird. Dies ist zum Beispiel die Essenz der template pattern. Solche Dinge können mit Delegation nicht leicht gemacht werden. Auf der anderen Seite ist dies genau das, was die Verwendung von Vererbung schwierig macht. Es bedarf einer mentalen Wendung, um zu verstehen, wo eine polymorphe Verteilung abläuft und welche Auswirkungen die Übersteuerung von Methoden hat.

Es gibt einige bekannte Fallstricke über die Vererbung und die Zerbrechlichkeit kann es in das Design einführen. Vor allem, wenn sich die Klassenhierarchie entwickelt. Es kann auch Probleme mit equality in hashCode und equals geben, wenn Vererbung verwendet wird. Aber auf der anderen Seite ist es immer noch eine sehr elegante Art, einige Probleme zu lösen.

Auch wenn Vererbung mit Delegation ersetzt werden kann, man kann man argumentieren, dass sie noch andere Ziel erreichen und sich gegenseitig ergänzen - sie haben nicht die gleichen Absicht vermitteln, die nicht durch reine technische erfasst Gleichwertigkeit.

(Meine Theorie ist, dass wenn jemand anfängt, OO zu tun, wir versucht sind, Vererbung zu verwenden, weil es wie ein Merkmal der Sprache wahrgenommen wird.Dann lernen wir Delegation, die Muster/Ansatz ist und wir lernen, es auch zu mögen. Nach einiger Zeit finden wir eine Balance zwischen beiden und entwickeln einen Sinn für Intuition, von dem man in diesem Fall besser ist. Nun, wie Sie sehen können, Ich mag immer noch beide, und beide verdienen gewisse Vorsicht vor eingeführt.)

Einige Literatur

Vererbung und Delegation sind alternative Methoden für inkrementelle Definition und Freigabe. Es hat gemeinhin geglaubt, dass Delegation ein leistungsfähigeres Modell zur Verfügung stellt. Dieses Papier zeigt, dass es ein "natürliches" Modell der Vererbung gibt, das alle Eigenschaften von Delegation erfasst. Unabhängig davon, bestimmte Einschränkungen für die Fähigkeit Delegation zur Erfassung der Vererbung sind demonstriert. Schließlich wird ein neues Framework beschrieben, das sowohl die Delegierung als auch die Vererbung vollständig erfasst, und einige der Verästelungen dieses hybriden Modells werden untersucht.

Eine der faszinierendsten-und am gleichzeitig problematischsten-Vorstellungen in objektorientierten Programmierung ist Vererbung. Die Vererbung ist allgemein als Merkmal angesehen, dass objektorientierte Programmierung von anderen modernen Programmierparadigmen unterscheiden, aber die Forscher selten einig über die Bedeutung und Nutzung. [...]

Wegen der starken Kopplung der Klassen und die Verbreitung von nicht benötigten Klassenmitgliedern induzierte durch Vererbung, zu dem Vorschlag Zusammensetzung und Delegation anstelle hat alltäglich werden. Die Darstellung eines entsprechenden Refactorings in der Literatur lässt vermuten, dass eine solche Transformation eine einfache Aufgabe ist. [...]

+0

Ist Teil dieser Auszüge aus einem veröffentlichten Papier ? Wenn ja, wäre ich an einem Link oder Doi interessiert. –

+2

Die ersten Absätze gehören mir und sind meiner Meinung nach. Dann gibt es 3 Verweise auf Artikel zu dem Thema, für das ich den Abstract zitiert habe. Das Doi kann in den Links gefunden werden, die alle auf portal.acm.org verweisen. Tut mir leid, wenn das nicht klar war. Lassen Sie mich wissen, wenn Sie sie nicht als pdf finden können. – ewernli

+0

Vielen Dank, ich schätze die Klarstellung. –

3

Zusammensetzung Leben nicht vermasseln kann als Erbe der Fall ist, wenn eine schnelle Waffe Programmierer versuchen, Probleme zu lösen, indem Methoden hinzufügen und Hierarchien erweitern (und nicht einen Gedanken an natürlichen Hierarchien geben)

Zusammensetzung kann nicht Ergebnis in seltsame Diamanten, die Wartungsteams Nachtöl zu verkratzen verursachen ihre Köpfe

Vererbung war das Wesen der Diskussion in GOF Design-Muster, die nicht die gleichen gewesen wären, wenn Programmierer Zusammensetzung in erster Linie verwendet.

+0

Kennzeichnen Sie mich als Troll, aber ich habe all diese Fehler gemacht, bevor ich von ihnen gelernt habe. – questzen

1

Betrachten wir ein GUI-Toolkit.

Ein Edit-Steuerelement ist ein Fenster, sollte es die Nähe des Fensters erben/enable/Malfunktionen - ein Fenster nicht enthält.

Dann wird eine Rich-Text-Kontrolle sollte der Edit-Steuerelemente enthält speichern/lesen/Ausschneiden/Einfügen-Funktionen wäre es sehr schwierig sein, zu verwenden, wenn es nur ein Fenster und ein Edit-Steuerelement enthalten ist.

+0

Wenn ich über GUI-Klassen spreche, ist es meiner Meinung nach zu einfach, die visuelle Komposition mit der Klassenzusammensetzung zu verwechseln. Man kann IWindow Deklarationssignaturen für fensterartige Objekte erstellen, und Fenster, um Fenster tatsächlich zu zeichnen und Ereignisse zu behandeln. Erstellen Sie dann IEditControl und EditControl, die IWindow implementieren. EditControl delegiert seine IWindow-Verantwortlichkeiten einfach an das Window-Objekt. Öffentlich sieht EditControl wie jedes andere IWindow aus und funktioniert wie Fenster mit EditControl-Verzierungen. Zuletzt implementiert RichTextControl IEditControl, das an ein eigenes EditControl-Objekt delegiert wird. Das Muster ist verkettbar. –

+1

Richtig, die meisten Vererbungsbeispiele (Mitarbeiterklassen usw.) werden besser als Komposition ausgeführt. Aber gui Toolkits profitieren wirklich von der Vererbung (IMHO) –

+2

Hochachtungsvoll: Mein Unternehmen unterhält ein umfangreiches Altsystem in .Net; Im Beispiel von MSFT mit WinForms verwendeten die ursprünglichen Entwickler viele Vererbung, um UI-Klassen zu implementieren. Wir haben 18 einzigartige (wenn auch ähnliche) Kombinationsfelder, 12 Basisformen und eine Vererbungshierarchie mit bis zu 8 Ebenen über dem CLI. Unser UI-Framework ist so ein Irrgarten, so inkonsistent und zerbrechlich, dass das aktuelle Entwicklerteam vor der geringsten Veränderung Angst hat. Aber oft entbehrt dieser Code allen neuen Anforderungen ... Die Komposition hätte bedeutet, dass wir Features nach Bedarf auswählen und auswählen können, anstatt endlos neue Kombinationen zu erstellen. –

1

Ich kann über dieses falsch sein, aber ich werde es trotzdem sagen, und wenn jemand einen Grund, warum ich bin falsch hat, bitte mit einem Kommentar nur reagieren und nicht nach unten stimmen mich. Es gibt eine Situation, an die ich denken kann, wo die Vererbung der Komposition überlegen ist.

Angenommen, ich eine Closed-Source-Widget-Bibliothek habe ich in einem Projekt bin mit (dh die Details der Implementierung ist mir ein Rätsel, außer dem, was dokumentiert ist). Angenommen, jedes Widget kann untergeordnete Widgets hinzufügen. Mit Vererbung konnte ich die Widget-Klasse erweitern, um ein CustomWidget zu erstellen, und dann CustomWidget als untergeordnetes Widget eines anderen Widgets in der Bibliothek hinzufügen. Dann ist mein Code ein CustomWidget hinzufügen würde wie folgt aussehen:

Widget baseWidget = new Widget(); 
CustomWidget customWidget = new CustomWidget(); 
baseWidget.addChildWidget(customWidget); 

Sehr sauber, und hält in Übereinstimmung mit den Konventionen der Bibliothek für Kind-Widgets hinzufügen. Jedoch mit der Zusammensetzung, wäre es um so etwas sein:

Widget baseWidget = new Widget(); 
CustomWidget customWidget = new CustomWidget(); 
customWidget.addAsChildToWidget(baseWidget); 

Nicht so sauber, und bricht auch die Konventionen der Bibliothek

Jetzt bin ich nicht sagen, dass Sie können nicht Beende dies mit der Komposition (mein Beispiel zeigt, dass du es sehr deutlich kannst), es ist einfach nicht unter allen Umständen ideal und kann dazu führen, dass Konventionen und andere, eher visuell unvorteilhafte Problemumgehungen gebrochen werden.

+2

Sie betonten Widget war in einer externen, geschlossenen Quellbibliothek. Wenn Widget.addChildWidget() ein Argument vom Typ Widget verwendet, ist die IMO-Vererbung die einzig sinnvolle Option. Die Bibliothek hat Eltern, die untergeordnete Elemente verfolgen, aber Ihr Kompositionsbeispiel erfordert CustomWidgets, um ihre übergeordneten Elemente zu verfolgen, anstatt von ihnen verfolgt zu werden. (Yuck!) Wenn Widget.addChildWidget() ein Argument vom Typ IWidget verwendet, könnte die Komposition noch funktionieren, da CustomWidget IWidget implementieren kann, während einige Verhaltensweisen auf ein privates Widget verschoben werden. BTW, Die wahre Kraft der Interface Composition kommt mit IOC & DI. –

-1

Ja. Es ist Laufzeittyp-Identifikation (RTTI).

+0

Wenn Sie RTTI benötigen, verletzen Sie ehrlich gesagt DIP. Aber mäh. Lassen Sie eine Schnittstelle eine 'getType()' oder ähnliche Funktion deklarieren und implementieren Sie die Schnittstelle. Jetzt haben Sie Ihre eigene Definition von "Typ", die Sie in alles, was Sie mögen, formen können und die beteiligten Klassen müssen nicht miteinander verwandt sein. – cHao

Verwandte Themen