Je mehr ich über verschiedene Programmierparadigmen wie die funktionale Programmierung lerne, desto mehr beginne ich, die Weisheit von OOP-Konzepten wie Vererbung und Polymorphismus in Frage zu stellen. Ich habe zum ersten Mal in der Schule etwas über Vererbung und Polymorphismus gelernt, und damals schien Polymorphismus eine wunderbare Möglichkeit zu sein, generischen Code zu schreiben, der eine einfache Erweiterbarkeit ermöglichte.
Aber angesichts der dynamischen und statischen Typisierung von Enten sowie funktionaler Merkmale wie Funktionen höherer Ordnung habe ich begonnen, Vererbung und Polymorphismus als eine unnötige Einschränkung zu betrachten, die auf einem fragilen Satz von Beziehungen zwischen Objekten beruht. Die allgemeine Idee hinter Polymorphismus ist, dass Sie eine Funktion einmal schreiben und später Ihrem Programm neue Funktionen hinzufügen können, ohne die ursprüngliche Funktion zu ändern. Sie müssen lediglich eine weitere abgeleitete Klasse erstellen, die die erforderlichen Methoden implementiert.
Dies ist jedoch viel einfacher durch Eingabe von Enten zu erreichen, unabhängig davon, ob es sich um eine dynamische Sprache wie Python oder eine statische Sprache wie C ++ handelt.
Betrachten Sie als Beispiel die folgende Python-Funktion, gefolgt von ihrem statischen C ++ - Äquivalent:
def foo(obj):
obj.doSomething()
template <class Obj>
void foo(Obj& obj)
{
obj.doSomething();
}
Das OOP-Äquivalent wäre etwa der folgende Java-Code:
public void foo(DoSomethingable obj)
{
obj.doSomething();
}
Der Hauptunterschied besteht natürlich darin, dass die Java-Version die Erstellung einer Schnittstelle oder einer Vererbungshierarchie erfordert, bevor sie funktioniert. Die Java-Version ist daher aufwändiger und weniger flexibel. Außerdem finde ich, dass die meisten Vererbungshierarchien in der Praxis etwas instabil sind. Wir haben alle die erfundenen Beispiele für Formen und Tiere gesehen, aber in der realen Welt ist es schwierig, eine Arbeit zu erledigen, wenn sich die Geschäftsanforderungen ändern und neue Funktionen hinzugefügt werden, bevor Sie die "Ist-Eine" -Beziehung zwischen ihnen wirklich ausdehnen müssen Unterklassen oder modifizieren / refaktorieren Sie Ihre Hierarchie, um weitere Basisklassen oder Schnittstellen einzubeziehen, um neuen Anforderungen gerecht zu werden. Mit Duck Typing brauchen Sie sich keine Gedanken über das Modellieren zu machen - Sie müssen sich nur um das Modellieren kümmern Funktionalität Sie benötigen.
Vererbung und Polymorphismus sind jedoch so beliebt, dass ich bezweifle, dass es übertrieben wäre, sie als die vorherrschende Strategie für Erweiterbarkeit und Wiederverwendung von Code zu bezeichnen. Warum sind Vererbung und Polymorphismus so erfolgreich? Übersehe ich einige gravierende Vorteile, die Vererbung / Polymorphismus gegenüber dem Typisieren von Enten haben?
quelle
obj
ich keinedoSomething
Methode hätte? Wird eine Ausnahme ausgelöst? Passiert nichtsAntworten:
Ich stimme Ihnen größtenteils zu, aber zum Spaß spiele ich Devil's Advocate. Explizite Schnittstellen bieten einen zentralen Ort, an dem Sie nach einem explizit formal festgelegten Vertrag suchen und erfahren, was ein Typ tun soll. Dies kann wichtig sein, wenn Sie nicht der einzige Entwickler eines Projekts sind.
Darüber hinaus können diese expliziten Schnittstellen effizienter implementiert werden als die Eingabe von Enten. Ein virtueller Funktionsaufruf hat kaum mehr Overhead als ein normaler Funktionsaufruf, außer dass er nicht eingebunden werden kann. Das Tippen von Enten ist mit erheblichem Aufwand verbunden. Die strukturelle Typisierung im C ++ - Stil (unter Verwendung von Vorlagen) kann große Mengen an aufgeblähter Objektdatei erzeugen (da jede Instanziierung auf Objektdateiebene unabhängig ist) und funktioniert nicht, wenn Sie zur Laufzeit Polymorphismus benötigen, nicht zur Kompilierungszeit.
Fazit: Ich bin damit einverstanden, dass Vererbung und Polymorphismus im Java-Stil eine PITA sein können und Alternativen häufiger verwendet werden sollten, aber sie haben immer noch ihre Vorteile.
quelle
Vererbung und Polymorphismus sind weit verbreitet, da sie bei bestimmten Arten von Programmierproblemen funktionieren.
Es ist nicht so, dass sie in den Schulen weit verbreitet sind, das ist umgekehrt: Sie werden in den Schulen weit verbreitet unterrichtet, weil die Leute (auch bekannt als der Markt) festgestellt haben, dass sie besser funktionieren als die alten Werkzeuge, und deshalb haben die Schulen angefangen, sie zu unterrichten. [Anekdote: Als ich zum ersten Mal OOP lernte, war es äußerst schwierig, ein College zu finden, das eine OOP-Sprache unterrichtete. Zehn Jahre später war es schwierig, ein College zu finden, das keine OOP-Sprache unterrichtete.]
Du sagtest:
Ich sage:
Nein, das ist es nicht
Was Sie beschreiben, ist nicht Polymorphismus, sondern Vererbung. Kein Wunder, dass Sie OOP-Probleme haben! ;-)
Sichern Sie einen Schritt: Polymorphismus ist ein Vorteil der Nachrichtenübermittlung. es bedeutet einfach, dass jedes Objekt frei ist, auf eine Nachricht auf seine eigene Weise zu antworten.
Also ... Duck Typing ist (oder vielmehr ermöglicht) Polymorphismus
Der Kern Ihrer Frage scheint nicht zu sein, dass Sie OOP nicht verstehen oder dass Sie es nicht mögen , sondern dass Sie es nicht mögen, Schnittstellen zu definieren . Das ist in Ordnung, und solange Sie vorsichtig sind, werden die Dinge gut funktionieren. Der Nachteil ist, dass Sie einen Fehler - beispielsweise das Weglassen einer Methode - erst zur Laufzeit feststellen können.
Das ist eine statische vs dynamische Sache, eine Diskussion, die so alt ist wie Lisp und nicht auf OOP beschränkt ist.
quelle
Warum?
Die Vererbung (mit oder ohne Eingabe von Enten) sichert die Wiederverwendung einer gemeinsamen Funktion. Wenn es üblich ist, können Sie sicherstellen, dass es in Unterklassen konsistent wiederverwendet wird.
Das ist alles. Es gibt keine "unnötige Einschränkung". Es ist eine Vereinfachung.
Ähnlich ist Polymorphismus das, was "Ententypisierung" bedeutet. Gleiche Methoden. Viele Klassen mit identischer Schnittstelle, aber unterschiedlichen Implementierungen.
Es ist keine Einschränkung. Es ist eine Vereinfachung.
quelle
Vererbung wird missbraucht, aber auch das Tippen von Enten. Beides kann und kann zu Problemen führen.
Mit starker Eingabe erhalten Sie viele "Komponententests", die zur Kompilierungszeit durchgeführt werden. Beim Entenschreiben muss man sie oft schreiben.
quelle
Das Gute daran, Dinge in der Schule zu lernen, ist, dass man sie lernt. Das nicht so Gute ist, dass Sie sie vielleicht ein wenig zu dogmatisch akzeptieren, ohne zu verstehen, wann sie nützlich sind und wann nicht.
Wenn Sie es dann dogmatisch gelernt haben, können Sie später ebenso dogmatisch in die andere Richtung "rebellieren". Das ist auch nicht gut.
Wie bei solchen Ideen ist es am besten, einen pragmatischen Ansatz zu wählen. Entwickeln Sie ein Verständnis dafür, wo sie passen und wo nicht. Und ignorieren Sie alle Möglichkeiten, die sie überverkauft haben.
quelle
Ja, statische Typisierung und Schnittstellen sind Einschränkungen. Aber alles, was seit der Erfindung der strukturierten Programmierung (dh "als schädlich eingestuft") passiert ist, hat uns gezwungen, uns einzuschränken. Onkel Bob hat dies in seinem Videoblog hervorragend aufgenommen .
Nun kann man argumentieren, dass Verengung schlecht ist, aber andererseits bringt sie Ordnung, Kontrolle und Vertrautheit in ein ansonsten sehr komplexes Thema.
Die Lockerung von Einschränkungen durch (erneute) Einführung dynamischer Typisierung und sogar direkten Speicherzugriff ist ein sehr leistungsfähiges Konzept, das es jedoch für viele Programmierer schwieriger machen kann, damit umzugehen. Vor allem Programmierer waren es gewohnt, sich für einen Großteil ihrer Arbeit auf den Compiler und die Typensicherheit zu verlassen.
quelle
Vererbung ist eine sehr starke Beziehung zwischen zwei Klassen. Ich denke nicht, dass Java stärker ist. Deshalb sollten Sie es nur verwenden, wenn Sie es ernst meinen. Öffentliche Vererbung ist eine "ist-eine" Beziehung, keine "normalerweise ist-eine". Es ist wirklich sehr, sehr einfach, Vererbung zu überbeanspruchen und mit einem Durcheinander fertig zu werden. In vielen Fällen wird die Vererbung verwendet, um "has-a" oder "takes-functional-from-a" darzustellen, und dies geschieht normalerweise besser durch Komposition.
Polymorphismus ist eine direkte Konsequenz der "Ist-Ist" -Beziehung. Wenn Derived von Base erbt, ist jede Derived-Base eine Base. Daher können Sie eine Derived überall dort verwenden, wo Sie eine Base verwenden möchten. Wenn dies in einer Vererbungshierarchie keinen Sinn ergibt, ist die Hierarchie falsch und es wird wahrscheinlich zu viel vererbt.
Duck Typing ist eine nette Funktion, aber der Compiler warnt Sie nicht, wenn Sie es missbrauchen. Wenn Sie zur Laufzeit keine Ausnahmen behandeln müssen, müssen Sie sich vergewissern, dass Sie jedes Mal die richtigen Ergebnisse erzielen. Es kann einfacher sein, nur eine statische Vererbungshierarchie zu definieren.
Ich bin kein wirklicher Fan des statischen Tippens (ich halte es oft für eine Form der vorzeitigen Optimierung), aber es beseitigt einige Fehlerklassen, und viele Leute denken, diese Klassen sind es wert, beseitigt zu werden.
Wenn Sie dynamisches Schreiben und Entenschreiben besser mögen als statisches Schreiben und definierte Vererbungshierarchien, ist das in Ordnung. Der Java-Weg hat jedoch seine Vorteile.
quelle
Ich habe festgestellt, dass ich mit zunehmender Verwendung von C # -Verschlüssen weniger traditionelle OOP-Operationen durchführe. Früher war die Vererbung die einzige Möglichkeit, die Implementierung einfach zu teilen. Ich denke, sie wurde häufig überstrapaziert und die konzeptionellen Grenzen wurden zu weit verschoben.
Während Sie können in der Regel Verschlüsse verwenden die meisten zu tun , was Sie mit Vererbung tun würde, kann es auch hässlich.
Grundsätzlich ist es eine Situation, in der das richtige Werkzeug für den Job ist: Traditionelle OOP können sehr gut funktionieren, wenn Sie ein Modell haben, das dazu passt, und Verschlüsse können sehr gut funktionieren, wenn Sie dies nicht tun.
quelle
Die Wahrheit liegt irgendwo in der Mitte. Ich mag, wie C # 4.0 als statisch typisierte Sprache "Enten-Typisierung" durch "dynamisches" Schlüsselwort unterstützt.
quelle
Vererbung, auch wenn die Vernunft aus FP-Sicht ein großartiges Konzept ist; Dies spart nicht nur viel Zeit, sondern gibt auch der Beziehung zwischen bestimmten Objekten in Ihrem Programm eine Bedeutung.
Hier hat die Klasse
GoldenRetriever
das gleicheSound
wieDog
kostenlos danke an Vererbung.Ich werde dasselbe Beispiel mit meinem Haskell-Level schreiben, damit Sie den Unterschied erkennen können
Hier kommt man nicht umhin, angeben
sound
zu müssenGoldenRetriever
. Das Einfachste im Allgemeinen wäre zuaber nur imagen wenn du 20 funktionen hast! Wenn es einen Haskell-Experten gibt, zeigen Sie uns bitte einen einfacheren Weg.
Das heißt, es wäre großartig, gleichzeitig Mustervergleich und Vererbung zu haben, wobei eine Funktion standardmäßig die Basisklasse verwendet, wenn die aktuelle Instanz nicht über die Implementierung verfügt.
quelle
Animal
ist das Anti-Tutorial von OOP.