Ich habe einen Code, in dem ein gutes Vererbungsmodell nicht mehr funktioniert, und ich versuche zu verstehen, warum und wie das Problem behoben werden kann. Stellen Sie sich vor, Sie haben eine Zoo-Hierarchie mit:
class Animal
class Parrot : Animal
class Elephant : Animal
class Cow : Animal
etc.
Sie haben Ihre Methoden eat (), run () usw. und alles ist gut. Dann kommt eines Tages jemand vorbei und sagt: Unsere CageBuilder-Klasse funktioniert hervorragend und verwendet animal.weight () und animal.height (), mit Ausnahme des neuen afrikanischen Bisons, der zu stark ist und die Wand zertrümmern kann eine weitere Eigenschaft für die Animal-Klasse - isAfricanBizon (). Verwenden Sie diese Eigenschaft bei der Auswahl des Materials und überschreiben Sie sie nur für die AfricanBizon-Klasse. Die nächste Person kommt und macht etwas Ähnliches, und als nächstes wissen Sie, dass Sie all diese Eigenschaften haben, die für eine Teilmenge der Hierarchie in der Basisklasse spezifisch sind.
Was ist ein guter Weg, um solchen Code zu verbessern / umzugestalten? Eine Alternative wäre hier, nur dynamic_casts zu verwenden, um nach den Typen zu suchen, aber das überfrachtet die Anrufer und fügt überall eine Menge Wenn-Dann-Anderer hinzu. Sie können hier spezifischere Schnittstellen haben, aber wenn Sie nur die Basisklassenreferenz haben, hilft das auch nicht viel. Irgendwelche anderen Vorschläge? Beispiele?
Vielen Dank!
quelle
Antworten:
Es scheint, als ob das Problem darin besteht, RequiresConcreteWall () nicht zu implementieren, sondern einen Flag-Aufruf IsAfricanBison () zu implementieren und dann die Logik dahingehend zu verschieben, ob sich die Wand außerhalb des Bereichs der Klasse ändern soll oder nicht. Ihre Klassen sollten Verhalten und Anforderungen offenlegen, nicht Identität; Ihre Konsumenten dieser Klassen sollten nach dem arbeiten, was ihnen gesagt wird, und nicht nach dem, was sie sind.
quelle
isAfricanBizon () ist nicht generisch. Angenommen, Sie erweitern Ihre Tierfarm mit einem Hyppopotamus, der ebenfalls zu stark ist, aber von isAfricanBizon () wahr zurückgegeben wird, um den richtigen Effekt zu erzielen, wäre einfach albern.
Sie möchten der Schnittstelle immer Methoden hinzufügen, die die spezifische Frage beantworten. In diesem Fall wäre dies etwa stärke ().
quelle
strength
könnte eine Methode abgefragt werdenmaterial.canHold(animal)
, die eine saubere Art der Unterstützung anderer Arten von Material als zulässtConcreteWall
.Ich denke, Ihr Problem ist Folgendes: Sie haben verschiedene Clients der Bibliothek, die nur an einer Teilmenge der Hierarchie interessiert sind, denen jedoch ein Zeiger / Verweis auf die Basisklasse übergeben wurde. Das ist in der Tat das Problem, das dynamic_cast <> lösen muss.
Es ist eine Frage des Designs der Clients, die Verwendung von dynamic_cast <> zu minimieren. Sie sollten es verwenden, um festzustellen, ob für das Objekt eine spezielle Behandlung erforderlich ist, und wenn ja, alle Operationen an der herabgegossenen Referenz durchführen.
Wenn Sie über Funktionssammlungen vom Typ "Mix-In" verfügen, die für mehrere separate Unterhierarchien gelten, möchten Sie möglicherweise das von Java und C # verwendete Schnittstellenmuster verwenden. Verfügen Sie über eine virtuelle Basisklasse, die eine rein virtuelle Klasse ist, und verwenden Sie dynamic_cast <>, um festzustellen, ob eine Instanz eine Implementierung dafür bereitstellt.
quelle
Eine Sache, die Sie tun können, ist, die explizite Prüfung des Typs
isAfricanBison()
durch die Prüfung der Eigenschaften zu ersetzen, an denen Sie tatsächlich interessiert sind, dhisTooStrong()
.quelle
Tiere sollten sich nicht um Betonwände kümmern. Vielleicht können Sie es mit einfachen Werten ausdrücken.
Ich vermute, dass das nicht tragfähig ist. Das ist jedoch das Problem mit Spielzeugbeispielen.
Ich würde auf keinen Fall RequiresConcreteWalls () oder Linien und Linien von dynamischen Zeigern sehen wollen.
Dies ist normalerweise eine kostengünstige Lösung. Es ist einfach zu pflegen und zu konzipieren. Und wirklich, das Problem besagt, dass es sowieso an den Tiertyp gebunden ist.
Dies schließt auch nicht aus, dass Sie gemeinsam genutzten Code verwenden. Verschmutzen Sie Animal nur ein bisschen.
Aber wie der Käfig gebaut wird, kann eine Politik eines anderen Systems sein, und vielleicht haben Sie mehr als eine Art Käfigbauer pro Tier. Es gibt viele seltsame und verschlungene Kombinationen, die Sie sich einfallen lassen können.
Ich habe Component Based Design zu guten Zwecken verwendet. Das Hauptproblem dabei ist, dass es problematisch sein kann, wenn das Eigentum von Animal geteilt wird. Wie man es vermeidet, Destruktoren einzuschleudern, ist der Schmerzpunkt.
Double Dispatch ist eine weitere Option, obwohl ich immer zurückhaltend war, mich darauf einzulassen.
Darüber hinaus ist das Problem schwer zu erraten.
quelle
Na sicher haben alle Tiere die inhärente Eigenschaft von
attemptEscape()
. Während einige der Methodenfalse
in allen Szenarien ein Ergebnis liefern können, können andere aufgrund von Heuristiken ihrer anderen intrinsischen Merkmale wiesize
und eine Chance habenweight
. Dann wird es sicherlich irgendwannattemptEscape()
trivial, da es mit Sicherheit zurückkehren wirdtrue
.Ich fürchte, ich verstehe Ihre Frage nicht ganz. Alle Tiere haben verwandte Handlungen und Eigenschaften. Tierspezifische sollten dort eingeführt werden, wo es passt. Der Versuch, Bison direkt mit Papageien in Beziehung zu setzen, ist keine gute Vererbung und sollte in einem ordnungsgemäßen Design eigentlich kein Problem darstellen.
quelle
Eine andere Möglichkeit wäre die Verwendung einer Fabrik, in der für jedes Tier geeignete Käfige hergestellt werden. Ich denke, dies kann besser sein, wenn die Bedingungen für jeden von ihnen sehr unterschiedlich sind. Aber wenn es nur diese eine Bedingung ist, wird die oben erwähnte
RequiresConcreteWall()
Methode es tun.quelle
wie wäre es mit RecommendCageType () als oppsed RequiresConcreteWall ()
quelle
Warum nicht so etwas tun?
class Animals { /***/ } class HeavyAnimals{} : Animals //The basic class for animals like the African Bison
Mit der Klasse HeavyAnimals können Sie die African Bison-Klasse erstellen, indem Sie die Klasse HeavyAnimals erweitern.
Nun können Sie die Elternklasse (Animals), mit der andere Basisklassen wie die HeavyAnimal-Klasse erstellt werden können, verwenden, um die African Bison-Klasse und andere Heavy Animals zu erstellen. Mit dem African Bison haben Sie nun Zugriff auf die Methoden und Eigenschaften der Klasse Animal (dies ist die Basis für alle Tiere) und auf die Klasse HeavyAnimals (dies ist die Basis für schwere Tiere).
quelle