Vor einiger Zeit habe ich eine Frage zu Stack Overflow über die Vererbung gestellt.
Ich habe gesagt, ich entwerfe Schachmaschine in OOP-Mode. Also erbe ich alle meine Stücke von Piece Abstract Class, aber die Vererbung geht immer noch. Lassen Sie mich durch Code zeigen
public abstract class Piece
{
public void MakeMove();
public void TakeBackMove();
}
public abstract class Pawn: Piece {}
public class WhitePawn :Pawn {}
public class BlackPawn:Pawn {}
Programmierer haben mein Design ein wenig über das Engineering hinaus gefunden und vorgeschlagen, farbige Stückklassen zu entfernen und Stückfarbe als Eigenschaftsmitglied wie unten zu halten.
public abstract class Piece
{
public Color Color { get; set; }
public abstract void MakeMove();
public abstract void TakeBackMove();
}
Auf diese Weise kann Piece seine Farbe erkennen. Nach der Implementierung habe ich gesehen, dass meine Implementierung wie folgt verläuft.
public abstract class Pawn: Piece
{
public override void MakeMove()
{
if (this.Color == Color.White)
{
}
else
{
}
}
public override void TakeBackMove()
{
if (this.Color == Color.White)
{
}
else
{
}
}
}
Jetzt sehe ich, dass die Farbeigenschaft if-Anweisungen in Implementierungen verursacht. Ich habe das Gefühl, wir brauchen bestimmte Farbstücke in der Vererbungshierarchie.
Hätten Sie in einem solchen Fall Klassen wie WhitePawn, BlackPawn oder würden Sie zum Design gehen, indem Sie die Color-Eigenschaft beibehalten?
Wie würden Sie ohne ein solches Problem mit dem Design beginnen wollen? Farbeigenschaft behalten oder Vererbungslösung haben?
Bearbeiten: Ich möchte angeben, dass mein Beispiel möglicherweise nicht vollständig mit dem Beispiel aus dem wirklichen Leben übereinstimmt. Bevor Sie also versuchen, Implementierungsdetails zu erraten, konzentrieren Sie sich einfach auf die Frage.
Ich frage eigentlich nur, ob die Verwendung der Color-Eigenschaft dazu führt, dass Anweisungen die Vererbung besser verwenden.
Antworten:
Stellen Sie sich vor, Sie entwerfen eine Anwendung, die Fahrzeuge enthält. Schaffst du so etwas?
Im wirklichen Leben ist Farbe eine Eigenschaft eines Objekts (entweder eines Autos oder einer Schachfigur) und muss durch eine Eigenschaft dargestellt werden.
Sind
MakeMove
undTakeBackMove
anders für Schwarze und Weiße? Überhaupt nicht: Die Regeln sind für beide Spieler gleich, was bedeutet, dass diese beiden Methoden für Schwarze und Weiße genau gleich sind , was bedeutet, dass Sie eine Bedingung hinzufügen, bei der Sie sie nicht benötigen.Auf der anderen Seite, wenn Sie haben
WhitePawn
undBlackPawn
, werde ich nicht überrascht sein, wenn Sie bald oder später schreiben werden:oder sogar so etwas wie:
quelle
direction
Eigenschaft zu haben und diese zum Verschieben zu verwenden, als die Farbeigenschaft zu verwenden. Farbe sollte idealerweise nur zum Rendern verwendet werden, nicht für die Logik.direction
Eigenschaft ein Boolescher Wert ist. Ich sehe nicht, was daran falsch ist. Was mich bis zu Freshbloods obigem Kommentar verwirrt hat, ist, warum er die Bewegungslogik basierend auf der Farbe ändern möchte. Ich würde sagen, das ist schwieriger zu verstehen, weil es keinen Sinn macht, dass Farbe das Verhalten beeinflusst. Wenn Sie nur unterscheiden möchten, welcher Spieler welches Stück besitzt, können Sie diese in zwei separate Sammlungen einteilen und die Notwendigkeit einer Eigenschaft oder Vererbung vermeiden. Die Figuren selbst könnten selig nicht wissen, zu welchem Spieler sie gehören.direction
oder vielleicht eineplayer
Eigenschaft anstellecolor
einer in der Spiellogik verwenden.white castle's moves differ from black's
- wie? Meinst du Rochade? Sowohl die königliche als auch die königliche Rochade sind für Schwarz und Weiß zu 100% symmetrisch.Was Sie sich fragen sollten, ist, warum Sie diese Aussagen in Ihrer Bauernklasse wirklich brauchen:
Ich bin mir ziemlich sicher, dass es ausreichen wird, einen kleinen Satz von Funktionen wie zu haben
in Ihrer Piece-Klasse und implementieren Sie alle Funktionen in
Pawn
(und die anderen Ableitungen vonPiece
) unter Verwendung dieser Funktionen, ohne jemals eine derif
obigen Anweisungen zu haben. Einzige Ausnahme kann die Funktion sein, die Bewegungsrichtung für einen Bauern anhand seiner Farbe zu bestimmen, da dies für Bauern spezifisch ist. In fast allen anderen Fällen benötigen Sie jedoch keinen farbspezifischen Code.Die andere interessante Frage ist, ob Sie wirklich verschiedene Unterklassen für verschiedene Arten von Stücken haben sollten. Das erscheint mir vernünftig und natürlich, da die Regeln für die Bewegungen für jedes Stück unterschiedlich sind und Sie dies sicherlich implementieren können, indem Sie für jede Art von Stück unterschiedliche überladene Funktionen verwenden.
Natürlich könnte man versuchen , dies in einer allgemeinere Art und Weise zu modellieren, indem Sie die Eigenschaften der Stücke aus ihrem bewegten Regeln zu trennen, aber das kann in einer abstrakten Klasse beenden
MovingRule
und eine Unterklasse HierarchieMovingRulePawn
,MovingRuleQueen
... würde ich anfangen es nicht , dass Weg, da es leicht zu einer anderen Form der Überentwicklung führen könnte.quelle
Die Vererbung ist nicht so nützlich, wenn die Erweiterung die externen Funktionen des Objekts nicht beeinträchtigt .
Die Color-Eigenschaft hat keinen Einfluss darauf, wie ein Piece-Objekt verwendet wird . Beispielsweise könnten alle Teile möglicherweise eine moveForward- oder moveBackwards-Methode aufrufen (auch wenn die Verschiebung nicht zulässig ist), aber die gekapselte Logik des Stücks verwendet die Color-Eigenschaft, um den Absolutwert zu bestimmen, der eine Vorwärts- oder Rückwärtsbewegung darstellt (-1 oder +) 1 auf der "y-Achse") Für Ihre API sind Ihre Ober- und Unterklassen identische Objekte (da sie alle dieselben Methoden verfügbar machen).
Persönlich würde ich einige Konstanten definieren, die jeden Stücktyp darstellen, und dann eine switch-Anweisung verwenden, um in private Methoden zu verzweigen, die mögliche Bewegungen für "diese" Instanz zurückgeben.
quelle
Board
Klasse (zum Beispiel) könnte dafür verantwortlich sein, diese Konzepte je nach Farbe des Stücks in -1 oder +1 umzuwandeln.Persönlich würde ich überhaupt nichts erben
Piece
, weil ich nicht glaube, dass Sie dadurch etwas gewinnen. Vermutlich ist es einem Stück eigentlich egal, wie es sich bewegt, nur welche Felder ein gültiges Ziel für seinen nächsten Zug sind.Vielleicht könnten Sie einen gültigen Bewegungsrechner erstellen, der die verfügbaren Bewegungsmuster für einen Stücktyp kennt und beispielsweise eine Liste gültiger Zielquadrate abrufen kann
quelle
Piece
". Es scheint, dass Piece nicht nur für die Berechnung von Bewegungen verantwortlich ist, sondern auch für die Aufteilung von Bewegungen als eigenständiges Anliegen. Zu bestimmen, welches Stück welchen Taschenrechner bekommt, wäre eine Frage der Abhängigkeitsinjektion, z. B.var my_knight = new Piece(new KnightMoveCalculator())
In dieser Antwort gehe ich davon aus, dass der Autor der Frage unter "Schach-Engine" "Schach-KI" versteht, nicht nur eine Bewegungsvalidierungs-Engine.
Ich denke, die Verwendung von OOP für Stücke und ihre gültigen Bewegungen ist aus zwei Gründen eine schlechte Idee:
Es ist meiner Meinung nach eine schlechte Idee, sich von Leistungsüberlegungen zu entfernen, einschließlich der Farbe des Stücks in der Typhierarchie. Sie befinden sich in Situationen, in denen Sie überprüfen müssen, ob zwei Teile unterschiedliche Farben haben, beispielsweise zum Erfassen. Das Überprüfen dieses Zustands kann einfach mithilfe einer Eigenschaft durchgeführt werden. Dies mit
is WhitePiece
-tests zu tun, wäre im Vergleich hässlicher.Es gibt noch einen weiteren praktischen Grund, um zu vermeiden, dass die Bewegungsgenerierung auf stückspezifische Methoden verschoben wird, nämlich dass verschiedene Teile gemeinsame Bewegungen haben, z. B. Türme und Königinnen. Um doppelten Code zu vermeiden, müssten Sie allgemeinen Code in eine Basismethode zerlegen und einen weiteren kostspieligen Aufruf durchführen.
Abgesehen davon, wenn es die erste Schach-Engine ist, die Sie schreiben, würde ich definitiv empfehlen, saubere Programmierprinzipien zu verwenden und die von Craftys Autor beschriebenen Optimierungstricks zu vermeiden. Der Umgang mit den Besonderheiten von Schachregeln (Castling, Promotion, En-Passant) und komplexen KI-Techniken (Hashing Boards, Alpha-Beta-Cut) ist schwierig genug.
quelle
WhichMovesArePossible
ist ein vernünftiger Ansatz.