Wenn Sie das Konzept des Polymorphismus verwenden, erstellen Sie eine Klassenhierarchie und rufen mit Hilfe der Elternreferenz die Schnittstellenfunktionen auf, ohne zu wissen, über welchen bestimmten Typ das Objekt verfügt. Das ist toll. Beispiel:
Sie haben eine Sammlung von Tieren und Sie rufen alle Tierfunktionen auf eat
und es ist Ihnen egal, ob es sich um einen Hund oder eine Katze handelt. Aber in der gleichen Klassenhierarchie haben Sie Tiere , die zusätzlichen haben - anders als geerbt und von Klasse implementiert Animal
, zum Beispiel makeEggs
, getBackFromTheFreezedState
und so weiter. In einigen Fällen möchten Sie in Ihrer Funktion möglicherweise den spezifischen Typ kennen, um zusätzliche Verhaltensweisen aufzurufen.
Zum Beispiel, wenn es Morgen ist und wenn es nur ein Tier eat
ist, dann rufst du an , sonst, wenn es ein Mensch ist, rufst du zuerst washHands
an getDressed
und dann erst an eat
. Wie gehe ich mit diesen Fällen um? Polymorphismus stirbt. Sie müssen den Typ des Objekts herausfinden, der sich wie ein Codegeruch anhört. Gibt es einen gemeinsamen Ansatz zur Behandlung dieser Fälle?
Eater
Schnittstelle mit dereat()
Methode definieren, kümmertHuman
es Sie als Client nicht, dass eine Implementierung diese zuerst aufrufen muss,washHands()
undgetDressed()
es handelt sich um Implementierungsdetails dieser Klasse. Wenn Ihnen als Kunde diese Tatsache am Herzen liegt, verwenden Sie höchstwahrscheinlich nicht das richtige Tool für den Job.getDressed
vor sich haben musseat
, was beim Mittagessen nicht der Fall ist. Je nach Ihren Umständen ist dieswashHands();if !dressed then getDressed();[code to actually eat]
möglicherweise der beste Weg, um dies für einen Menschen umzusetzen. Eine andere Möglichkeit ist was, wenn andere Dinge das erfordernwashHands
und / odergetDressed
genannt werden? Angenommen, Sie habenleaveForWork
? Möglicherweise müssen Sie den Programmfluss so strukturieren, dass er ohnehin schon lange vorher aufgerufen wird.Antworten:
Hängt davon ab. Leider gibt es keine generische Lösung. Denken Sie über Ihre Anforderungen nach und versuchen Sie herauszufinden, was diese Dinge tun sollten.
Sie sagten zum Beispiel morgens, dass verschiedene Tiere verschiedene Dinge tun. Wie wäre es, wenn Sie eine Methode
getUp()
oderprepareForDay()
ähnliches einführen? Dann können Sie mit dem Polymorphismus fortfahren und jedes Tier seine Morgenroutine ausführen lassen.Wenn Sie zwischen Tieren unterscheiden wollen, sollten Sie sie nicht wahllos in einer Liste ablegen.
Wenn nichts anderes funktioniert, können Sie das Besuchermuster ausprobieren , das eine Art Hack darstellt , um eine Art dynamisches Dispatching zu ermöglichen, bei dem Sie einen Besucher einreichen können, der typgenaue Rückrufe von Tieren erhält. Ich möchte jedoch betonen, dass dies ein letzter Ausweg sein sollte, wenn alles andere fehlschlägt.
quelle
Dies ist eine gute Frage und es ist die Art von Ärger, die viele Leute haben, wenn sie versuchen, zu verstehen, wie man OO benutzt. Ich denke, die meisten Entwickler haben damit zu kämpfen. Ich wünschte, ich könnte sagen, dass die meisten daran vorbeikommen, aber ich bin mir nicht sicher, ob das der Fall ist. Meiner Erfahrung nach verwenden die meisten Entwickler Pseudo-OO-Eigenschaftensäcke .
Lassen Sie mich zunächst klar sein. Das ist nicht deine Schuld. Die Art und Weise, wie OO gelehrt wird, ist in der Regel sehr fehlerhaft. Das
Animal
Beispiel ist der Haupttäter, IMO. Grundsätzlich sagen wir, lassen Sie uns über Objekte sprechen, was können sie tun. EineAnimal
Doseeat()
und es kannspeak()
. Super. Erstellen Sie nun einige Tiere und kodieren Sie, wie sie essen und sprechen. Jetzt weißt du OO, richtig?Das Problem ist, dass dies bei OO aus der falschen Richtung kommt. Warum gibt es Tiere in diesem Programm und warum müssen sie sprechen und essen?
Es fällt mir schwer, über eine wirkliche Verwendung für einen
Animal
Typ nachzudenken . Ich bin mir sicher, dass es das gibt, aber lassen Sie uns etwas diskutieren, von dem ich denke, dass es einfacher ist, darüber nachzudenken: eine Verkehrssimulation. Angenommen, wir möchten den Verkehr in verschiedenen Szenarien modellieren. Hier sind einige grundlegende Dinge, die wir brauchen, um das zu können.Wir können mit allen möglichen Dingen, wie Fußgängern und Zügen, tiefer gehen, aber wir werden es einfach halten.
Lassen Sie uns überlegen
Vehicle
. Welche Fähigkeiten benötigt das Fahrzeug? Es muss auf einer Straße fahren. Es muss in der Lage sein, bei Signalen anzuhalten. Es muss in der Lage sein, Kreuzungen zu navigieren.Das ist wahrscheinlich zu einfach, aber es ist ein Anfang. Jetzt. Was ist mit all den anderen Dingen, die ein Fahrzeug tun könnte? Sie können von einer Straße in einen Graben abbiegen. Gehört das zur Simulation? Nein, brauche es nicht. Einige Autos und Busse verfügen über eine Hydraulik, mit der sie springen oder knien können. Gehört das zur Simulation? Nein, brauche es nicht. Die meisten Autos verbrennen Benzin. Manche nicht. Ist das Kraftwerk Teil der Simulation? Nein, brauche es nicht. Radgröße? Brauche es nicht GPS Navigation? Infotainmentsystem? Ich brauche sie nicht.
Sie müssen nur das Verhalten definieren, das Sie verwenden möchten. Aus diesem Grund denke ich, ist es oft besser, OO-Schnittstellen aus dem Code zu erstellen, der mit ihnen interagiert. Sie beginnen mit einer leeren Schnittstelle und schreiben dann den Code, der die nicht vorhandenen Methoden aufruft. So wissen Sie, welche Methoden Sie für Ihre Benutzeroberfläche benötigen. Anschließend definieren Sie Klassen, die diese Verhaltensweisen implementieren. Verhaltensweisen, die nicht verwendet werden, sind irrelevant und müssen nicht definiert werden.
Der springende Punkt bei OO ist, dass Sie später neue Implementierungen dieser Schnittstellen hinzufügen können, ohne den aufrufenden Code zu ändern. Das funktioniert nur, wenn die Anforderungen des aufrufenden Codes bestimmen, was in der Schnittstelle abläuft. Es gibt keine Möglichkeit, alle Verhaltensweisen aller möglichen Dinge zu definieren, die später erdacht werden könnten.
quelle
TL; DR:
Stellen Sie sich eine Abstraktion und Methoden vor, die für alle Unterklassen gelten, und decken Sie alles ab, was Sie benötigen.
Bleiben wir zunächst bei Ihrem
eat()
Beispiel.Es ist eine Eigenschaft des Menschseins, dass Menschen als Voraussetzung für das Essen ihre Hände waschen und sich vor dem Essen anziehen wollen. Wenn Sie möchten, dass jemand zu Ihnen kommt, um mit Ihnen zu frühstücken, sagen Sie ihm nicht , er solle sich die Hände waschen und anziehen, er tut es auf eigene Faust, wenn Sie ihn einladen, oder er antwortet: "Nein, ich kann nicht kommen Ich habe meine Hände nicht gewaschen und bin noch nicht angezogen ".
Zurück zur Software:
Als
Human
Beispiel wird , ohne dass die Voraussetzungen nicht essen, würde ich das hatHuman
s‘eat()
Methode zu tunwashHands()
undgetDressed()
wenn das nicht geschehen ist. Es sollte nicht Ihre Aufgabe alseat()
Anrufer sein, über diese Besonderheit Bescheid zu wissen. Die Alternative für hartnäckige Menschen wäre, eine Ausnahme zu machen ("Ich bin nicht bereit zu essen!"), Wenn die Voraussetzungen nicht erfüllt sind, was Sie frustriert, aber zumindest darüber informiert, dass das Essen nicht funktioniert.Was ist
makeEggs()
?Ich würde empfehlen, Ihre Denkweise zu ändern. Sie möchten wahrscheinlich die geplanten Morgenaufgaben aller Wesen ausführen. Auch hier sollte es nicht Ihre Aufgabe sein, als Anrufer zu wissen, welche Aufgaben sie haben. Daher würde ich eine
doMorningDuties()
Methode empfehlen , die von allen Klassen implementiert wird.quelle
Die Antwort ist ganz einfach.
Sie müssen nicht damit umgehen, weil es keinen Zweck erfüllen würde. Eine Schnittstelle wird normalerweise in Abhängigkeit von ihrer Verwendung entworfen. Wenn Ihre Benutzeroberfläche kein Händewaschen definiert, ist es Ihnen als Aufrufer der Benutzeroberfläche egal. wenn du es getan hättest, hättest du es anders gestaltet.
Zum Beispiel im Pseudocode:
Jetzt implementieren Sie,
IMorningPerformer
umAnimal
nur zu essen, undHuman
implementieren es, um Hände zu waschen und sich anzuziehen. Der Aufrufer Ihrer MorningTime-Methode könnte sich weniger darum kümmern, ob es sich um einen Menschen oder ein Tier handelt. Alles, was es will, ist die morgendliche Routine, die jedes Objekt dank OO bewundernswert macht.Oder doch?
Warum nehmen wir das an? Ich denke, das könnte eine falsche Annahme sein.
Ja, normalerweise wird es mit einer sorgfältig entworfenen Klassen- oder Schnittstellenhierarchie gelöst. Beachten Sie, dass es im obigen Beispiel nichts gibt, was Ihrem Beispiel in der von Ihnen angegebenen Form widerspricht. Sie werden sich jedoch wahrscheinlich unzufrieden fühlen, da Sie weitere Annahmen getroffen haben, die Sie in der Frage zum Zeitpunkt des Schreibens nicht geschrieben haben , und diese Annahmen sind wahrscheinlich verletzt.
Es ist möglich, zu einem Kaninchenbau zu gehen, indem Sie Ihre Annahmen verschärfen und ich die Antwort ändere, um sie noch zu befriedigen, aber ich halte das nicht für nützlich.
Das Entwerfen guter Klassenhierarchien ist schwierig und erfordert viel Einblick in Ihre Geschäftsdomäne. Bei komplexen Domänen werden zwei, drei oder sogar mehr Iterationen durchlaufen, während das Verständnis der Interaktion zwischen verschiedenen Entitäten in der Unternehmensdomäne verfeinert wird, bis ein geeignetes Modell gefunden wird.
Hier fehlen vereinfachte Tierbeispiele. Wir wollen einfach lehren, aber das Problem, das wir zu lösen versuchen, ist nicht offensichtlich, bis Sie tiefer gehen, dh komplexere Überlegungen und Bereiche haben.
quelle