Il y a une fois, j'ai posé une question sur Stack Overflow sur l'héritage.
J'ai dit que je conçois un moteur d'échecs de façon OOP. J'hérite donc tous mes morceaux de la classe abstraite Piece mais l'héritage continue. Permettez-moi de montrer par code
public abstract class Piece
{
public void MakeMove();
public void TakeBackMove();
}
public abstract class Pawn: Piece {}
public class WhitePawn :Pawn {}
public class BlackPawn:Pawn {}
Les programmeurs ont trouvé ma conception un peu trop technique et ont suggéré de supprimer les classes de pièces colorées et de conserver la couleur des pièces en tant que membre de la propriété, comme ci-dessous.
public abstract class Piece
{
public Color Color { get; set; }
public abstract void MakeMove();
public abstract void TakeBackMove();
}
Ainsi, Piece peut connaître sa couleur. Après la mise en œuvre, j'ai vu ma mise en œuvre comme ci-dessous.
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
{
}
}
}
Maintenant, je vois que la propriété keep color provoque des instructions if dans les implémentations. Cela me fait sentir que nous avons besoin de pièces de couleur spécifiques dans la hiérarchie d'héritage.
Dans un tel cas, auriez-vous des classes comme WhitePawn, BlackPawn ou iriez-vous à la conception en conservant la propriété Color?
Sans avoir vu un tel problème, comment voudriez-vous commencer la conception? Conserver la propriété Couleur ou avoir une solution d'héritage?
Edit: je veux spécifier que mon exemple peut ne pas correspondre complètement à l'exemple de la vie réelle. Donc, avant d'essayer de deviner les détails de la mise en œuvre, concentrez-vous simplement sur la question.
En fait, je demande simplement si l'utilisation de la propriété Color entraînera une meilleure utilisation de l'héritage par les instructions?
Réponses:
Imaginez que vous conceviez une application qui contient des véhicules. Créez-vous quelque chose comme ça?
Dans la vraie vie, la couleur est une propriété d'un objet (une voiture ou une pièce d'échecs) et doit être représentée par une propriété.
Sont
MakeMove
etTakeBackMove
différents pour les noirs et les blancs? Pas du tout: les règles sont les mêmes pour les deux joueurs, ce qui signifie que ces deux méthodes seront exactement les mêmes pour les noirs et les blancs , ce qui signifie que vous ajoutez une condition où vous n'en avez pas besoin.D'un autre côté, si vous en avez
WhitePawn
etBlackPawn
je ne serai pas surpris si tôt ou tard vous écrirez:ou même quelque chose comme:
la source
direction
propriété et l'utiliser pour se déplacer que d'utiliser la propriété color. La couleur ne devrait idéalement être utilisée que pour le rendu, pas pour la logique.direction
propriété est un booléen. Je ne vois pas ce qui ne va pas avec ça. Ce qui m'a troublé à propos de la question jusqu'au commentaire de Freshblood ci-dessus, c'est pourquoi il voudrait changer la logique du mouvement en fonction de la couleur. Je dirais que c'est plus difficile à comprendre car cela n'a aucun sens que la couleur affecte le comportement. Si tout ce que vous voulez faire est de distinguer quel joueur possède quelle pièce, vous pouvez les mettre dans deux collections distinctes et éviter d'avoir besoin d'une propriété ou d'un héritage. Les pièces elles-mêmes pourraient parfaitement ignorer à quel joueur elles appartiennent.direction
ou peut-être uneplayer
propriété au lieu d'unecolor
dans la logique du jeu.white castle's moves differ from black's
- comment? Voulez-vous dire roque? Le roque côté roi et le roque côté reine sont 100% symétriques pour le noir et le blanc.Ce que vous devez vous demander, c'est pourquoi vous avez vraiment besoin de ces déclarations dans votre classe Pawn:
Je suis presque sûr qu'il suffira d'avoir un petit ensemble de fonctions comme
dans votre classe Piece et implémentez toutes les fonctions dans
Pawn
(et les autres dérivations dePiece
) en utilisant ces fonctions, sans jamais avoir l'une de cesif
instructions ci-dessus. La seule exception peut être la fonction pour déterminer la direction de déplacement d'un pion par sa couleur, car cela est spécifique aux pions, mais dans presque tous les autres cas, vous n'aurez pas besoin de code spécifique à la couleur.L'autre question intéressante est de savoir si vous devriez vraiment avoir différentes sous-classes pour différents types de pièces. Cela me semble raisonnable et naturel, car les règles de déplacement pour chaque pièce sont différentes et vous pouvez sûrement l'implémenter en utilisant différentes fonctions surchargées pour chaque type de pièce.
Bien sûr, on pourrait essayer de modéliser cela d'une manière plus générale, en séparant les propriétés des pièces de leurs règles en mouvement, mais cela peut se terminer par une classe abstraite
MovingRule
et une hiérarchie de sous - classeMovingRulePawn
,MovingRuleQueen
... je ne commencerais pas ce que car cela pourrait facilement conduire à une autre forme de suringénierie.la source
L'héritage n'est pas aussi utile lorsque l'extension n'affecte pas les capacités externes de l'objet .
La propriété Color n'affecte pas la façon dont un objet Piece est utilisé . Par exemple, toutes les pièces pourraient invoquer une méthode moveForward ou moveBackwards (même si le mouvement n'est pas légal), mais la logique encapsulée de la pièce utilise la propriété Color pour déterminer la valeur absolue qui représente un mouvement vers l'avant ou vers l'arrière (-1 ou + 1 sur l '"axe y"), en ce qui concerne votre API, votre superclasse et vos sous-classes sont des objets identiques (car ils exposent tous les mêmes méthodes).
Personnellement, je définirais des constantes qui représentent chaque type de pièce, puis j'utiliserais une instruction switch pour créer des méthodes privées renvoyant les mouvements possibles pour "cette" instance.
la source
Board
classe (par exemple) pourrait être en charge de convertir ces concepts en -1 ou +1 selon la couleur de la pièce.Personnellement, je n'hériterais de rien du
Piece
tout, car je ne pense pas que vous gagniez quoi que ce soit à cela. Vraisemblablement, une pièce ne se soucie pas vraiment de la façon dont elle se déplace, mais uniquement des cases qui sont une destination valide pour son prochain mouvement.Vous pourriez peut-être créer une calculatrice de déplacement valide qui connaît les modèles de mouvement disponibles pour un type de pièce et est capable de récupérer une liste de carrés de destination valides, par exemple
la source
Piece
tout". Il semble que Piece soit responsable de plus que de simples calculs de mouvements, ce qui est la raison pour laquelle le mouvement est divisé en une préoccupation distincte. Déterminer quelle pièce obtient quelle calculatrice serait une question d'injection de dépendance, par exemplevar my_knight = new Piece(new KnightMoveCalculator())
Dans cette réponse, je suppose que par "moteur d'échecs", l'auteur de la question signifie "AI d'échecs", pas simplement un moteur de validation de mouvement.
Je pense que l'utilisation de la POO pour les pièces et leurs mouvements valides est une mauvaise idée pour deux raisons:
S'éloigner des considérations de performance, y compris la couleur de la pièce dans la hiérarchie des types, est à mon avis une mauvaise idée. Vous vous retrouverez dans des situations où vous devrez vérifier si deux pièces ont des couleurs différentes, par exemple pour la capture. La vérification de cette condition se fait facilement à l'aide d'une propriété. Le faire en utilisant
is WhitePiece
-tests serait plus laid en comparaison.Il y a aussi une autre raison pratique pour éviter de déplacer la génération de mouvements vers des méthodes spécifiques aux pièces, à savoir que différentes pièces partagent des mouvements communs, par exemple des tours et des reines. Afin d'éviter le code en double, vous devez factoriser le code commun dans une méthode de base et avoir un autre appel coûteux.
Cela étant dit, si c'est le premier moteur d'échecs que vous écrivez, je vous conseillerais certainement d'aller avec des principes de programmation propres et d'éviter les astuces d'optimisation décrites par l'auteur de Crafty. Gérer les spécificités des règles d'échecs (roque, promotion, en passant) et des techniques d'IA complexes (planches de hachage, coupe alpha-bêta) est déjà assez difficile.
la source
WhichMovesArePossible
est une approche raisonnable.