it-swarm-eu.dev

Warum vermeiden Sie Java Vererbung "Erweitert"

Sagte Jame Gosling

"Sie sollten die Vererbung der Implementierung nach Möglichkeit vermeiden."

verwenden Sie stattdessen die Schnittstellenvererbung.

Aber warum? Wie können wir vermeiden, die Struktur eines Objekts mit dem Schlüsselwort "Erweitert" zu erben und gleichzeitig unseren Code objektorientiert zu gestalten?

Könnte jemand bitte ein objektorientiertes Beispiel geben, das dieses Konzept in einem Szenario wie "Bestellen eines Buches in einem Buchladen" veranschaulicht?

40
newbie

Gosling hat nicht gesagt, dass die Verwendung des Schlüsselworts extensives vermieden werden soll. Er hat nur gesagt, dass die Vererbung nicht als Mittel zur Wiederverwendung von Code verwendet werden soll.

Vererbung ist an sich nicht schlecht und ein sehr leistungsfähiges (wesentliches) Werkzeug zum Erstellen von OO - Strukturen. Wenn sie jedoch nicht richtig verwendet werden (dh wenn sie für etwas verwendet werden sonst als das Erstellen von Objektstrukturen) erstellt es Code, der sehr eng gekoppelt und sehr schwer zu pflegen ist.

Da die Vererbung zum Erstellen polymorpher Strukturen verwendet werden sollte, kann sie genauso einfach mit Schnittstellen durchgeführt werden, daher die Anweisung, beim Entwerfen Ihrer Objektstrukturen eher Schnittstellen als Klassen zu verwenden. Wenn Sie Extends als Mittel verwenden, um das Einfügen von Code von einem Objekt in ein anderes zu vermeiden, verwenden Sie ihn möglicherweise falsch und sollten besser mit einer speziellen Klasse bedient werden, um diese Funktionalität zu handhaben und diesen Code stattdessen durch Komposition freizugeben.

Lesen Sie über das Liskov-Substitutionsprinzip und verstehen Sie es. Wenn Sie eine Klasse (oder eine Schnittstelle) erweitern möchten, fragen Sie sich, ob Sie gegen das Prinzip verstoßen. Wenn ja, verwenden Sie die Vererbung höchstwahrscheinlich (ab) auf unnatürliche Weise. Es ist einfach zu machen und scheint oft das Richtige zu sein, aber es wird schwieriger, Ihren Code zu warten, zu testen und weiterzuentwickeln.

--- Bearbeiten Diese Antwort ist ein ziemlich gutes Argument, um die Frage allgemeiner zu beantworten.

38
Newtopian

In den frühen Tagen der objektorientierten Programmierung war die Vererbung der Implementierung der goldene Hammer für die Wiederverwendung von Code. Wenn in einer Klasse eine Funktionalität vorhanden war, die Sie benötigten, mussten Sie öffentlich von dieser Klasse erben (dies waren die Tage, an denen C++ häufiger als Java war), und Sie erhielten diese Funktionalität "kostenlos".

Außer, dass es nicht wirklich kostenlos war. Das Ergebnis waren extrem verschlungene Vererbungshierarchien, die kaum zu verstehen waren, einschließlich rautenförmiger Vererbungsbäume, die schwer zu handhaben waren. Dies ist Teil des Grundes, warum die Designer von Java entschieden haben, die Mehrfachvererbung von Klassen nicht zu unterstützen.

Goslings Rat (und andere Aussagen), als wäre es eine starke Medizin für eine ziemlich schwere Krankheit, und es ist möglich, dass einige Babys mit dem Badewasser weggeworfen wurden. Es ist nichts Falsches daran, mithilfe der Vererbung eine Beziehung zwischen Klassen zu modellieren, die wirklich is-a Ist. Wenn Sie jedoch nur eine Funktionalität aus einer anderen Klasse verwenden möchten, sollten Sie zuerst andere Optionen untersuchen.

9
JohnMcG

Die Vererbung hat in letzter Zeit einen ziemlich beängstigenden Ruf erlangt. Ein Grund, dies zu vermeiden, geht mit dem Entwurfsprinzip "Komposition über Vererbung" einher, das die Menschen grundsätzlich dazu ermutigt, Vererbung nicht zu verwenden, wenn dies nicht die wahre Beziehung ist, nur um etwas Code-Schreiben zu sparen. Diese Art der Vererbung kann dazu führen, dass einige Fehler schwer zu finden und zu beheben sind. Der Grund, warum Ihr Freund wahrscheinlich vorgeschlagen hat, stattdessen eine Schnittstelle zu verwenden, ist, dass Schnittstellen eine flexiblere und wartbarere Lösung für verteilte APIs bieten. Sie ermöglichen häufiger Änderungen an einer API, ohne den Benutzercode zu beschädigen, während das Offenlegen von Klassen häufig den Benutzercode beschädigt. Das beste Beispiel hierfür in .Net ist der Unterschied in der Verwendung zwischen List und IList.

6

Weil Sie nur eine Klasse erweitern können, während Sie viele Schnittstellen implementieren können.

Ich habe einmal gehört, dass Vererbung definiert, was Sie sind, aber Schnittstellen definieren eine Rolle, die Sie spielen können.

Schnittstellen ermöglichen auch eine bessere Nutzung des Polymorphismus.

Das könnte ein Teil des Grundes sein.

5
Mahmoud Hossam

Das wurde mir auch beigebracht und ich bevorzuge nach Möglichkeit Schnittstellen (natürlich verwende ich immer noch Vererbung, wo es Sinn macht).

Ich denke, es entkoppelt Ihren Code von bestimmten Implementierungen. Angenommen, ich habe eine Klasse namens ConsoleWriter, die an eine Methode zum Schreiben von etwas übergeben und in die Konsole geschrieben wird. Angenommen, ich möchte zum Ausdrucken in ein GUI-Fenster wechseln. Nun muss ich entweder die Methode ändern oder eine neue schreiben, die GUIWriter als Parameter verwendet. Wenn ich zunächst eine IWriter-Schnittstelle definieren würde und die Methode einen IWriter übernehmen würde, könnte ich mit dem ConsoleWriter (der die IWriter-Schnittstelle implementieren würde) beginnen und später eine neue Klasse namens GUIWriter (die auch die IWriter-Schnittstelle implementiert) schreiben und dann I. müsste nur die Klasse ausschalten, die übergeben wird.

Eine andere Sache (die für C # gilt, nicht sicher über Java) ist, dass Sie nur 1 Klasse erweitern können, aber viele Schnittstellen implementieren können. Nehmen wir an, ich hatte Klassen mit den Namen Teacher, MathTeacher und HistoryTeacher. Jetzt erstrecken sich MathTeacher und HistoryTeacher vom Lehrer, aber was ist, wenn wir eine Klasse wollen, die jemanden repräsentiert, der sowohl MathTeacher als auch HistoryTeacher ist? Es kann ziemlich chaotisch werden, wenn Sie versuchen, von mehreren Klassen zu erben, wenn Sie es jeweils nur einzeln tun können (es gibt Möglichkeiten, aber sie sind nicht genau optimal). Mit Schnittstellen können Sie zwei Schnittstellen haben, die IMathTeacher und IHistoryTeacher genannt werden, und dann eine Klasse haben, die sich vom Lehrer aus erstreckt und diese beiden Schnittstellen implementiert.

Ein Nachteil bei der Verwendung von Schnittstellen ist, dass ich manchmal sehe, dass Leute Code duplizieren (da Sie die Implementierung für jede Klasse erstellen müssen, um die Schnittstelle zu implementieren), es jedoch eine saubere Lösung für dieses Problem gibt, zum Beispiel die Verwendung von Dingen wie Delegaten (nicht sicher) was das Java äquivalent dazu ist).

Der Hauptgrund für die Verwendung von Schnittstellen über Vererbungen ist die Entkopplung des Implementierungscodes. Denken Sie jedoch nicht, dass Vererbung böse ist, da sie immer noch sehr nützlich ist.

1
ryanzec

Wenn Sie von einer Klasse erben, sind Sie nicht nur von dieser Klasse abhängig, sondern müssen auch alle impliziten Verträge berücksichtigen, die von Clients dieser Klasse erstellt wurden. Onkel Bob liefert ein großartiges Beispiel dafür, wie leicht es ist, das Liskov-Substitutionsprinzip mit dem grundlegenden Dilemma Quadrat erweitert Rechteck subtil zu verletzen. Schnittstellen haben, da sie keine Implementierung haben, auch keine versteckten Verträge.

0
Michael Brown