refactoriser le modèle de stratégie python pour utiliser une classe de base abstraite
Je suis tombé sur cette mise en œuvre de modèle de stratégie
https://github.com/jtortorelli/head-first-design-patterns-python/blob/master/src/python/chapter_1/adventure_game.py
class Character:
def __init__(self):
self.weapon_behavior = None
def set_weapon(self, weapon_behavior):
self.weapon_behavior = weapon_behavior
def fight(self):
self.weapon_behavior.use_weapon()
class Queen(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = KnifeBehavior()
class King(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = BowAndArrowBehavior()
class Troll(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = AxeBehavior()
class Knight(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = SwordBehavior()
class WeaponBehavior:
def use_weapon(self):
raise NotImplementedError
class KnifeBehavior(WeaponBehavior):
def use_weapon(self):
print("Stabby stab stab")
class BowAndArrowBehavior(WeaponBehavior):
def use_weapon(self):
print("Thwing!")
class AxeBehavior(WeaponBehavior):
def use_weapon(self):
print("Whack!")
class SwordBehavior(WeaponBehavior):
def use_weapon(self):
print("Thrust!")
knight = Knight()
king = King()
queen = Queen()
troll = Troll()
knight.fight()
king.fight()
queen.fight()
troll.fight()
Serait-il correct de le refactoriser de la manière suivante, en utilisant un ABC?
from abc import ABC, abstractmethod
class Character:
def __init__(self):
self.weapon_behavior = None
def set_weapon(self, weapon_behavior):
self.weapon_behavior = weapon_behavior
def fight(self):
self.weapon_behavior.use_weapon()
class Queen(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = KnifeBehavior()
class King(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = BowAndArrowBehavior()
class Troll(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = AxeBehavior()
class Knight(Character):
def __init__(self):
super().__init__()
self.weapon_behavior = SwordBehavior()
class WeaponBehavior(ABC):
@abstractmethod
def use_weapon(self, message):
print(message)
class KnifeBehavior(WeaponBehavior):
def use_weapon(self):
super().use_weapon("Stabby stab stab")
class BowAndArrowBehavior(WeaponBehavior):
def use_weapon(self):
super().use_weapon("Thwing!")
class AxeBehavior(WeaponBehavior):
def use_weapon(self):
super().use_weapon("Whack!")
class SwordBehavior(WeaponBehavior):
def use_weapon(self):
super().use_weapon("Thrust!")
knight = Knight()
king = King()
queen = Queen()
troll = Troll()
knight.fight()
king.fight()
queen.fight()
troll.fight()
Réponses
Seules les classes Character et WeaponBehaviour sont réellement utiles et aucune d'elles n'a besoin d'hériter de quoi que ce soit. Le reste peut être simplement des fonctions d'usine, car seuls les constructeurs diffèrent. Si un constructeur de classe fait autre chose que d'affecter des arguments aux propriétés, c'est probablement faux.
Le modèle de stratégie est basé sur la composition plutôt que sur l'héritage.
class Character:
def __init__(self, weapon):
self.weapon = weapon
def set_weapon(self, weapon):
self.weapon = weapon
def fight(self):
self.weapon.use()
def Queen():
return Character(Knife())
def King():
return Character(Bow())
class Weapon:
def __init__(self, message):
self.message = message
def use(self):
print(self.message)
def Knife():
return Weapon("Stabby stab stab")
def Bow():
return Weapon("Thwing!")
king = King()
queen = Queen()
king.fight()
queen.fight()
queen.set_weapon(Bow())
queen.fight()
Notez que j'ai supprimé la behaviorpartie des noms car cela me semblait un peu inutile.
J'ai également renommé use_weapon()en juste use()parce qu'il est appelé sur une weaponvariable, et donc cela semblait redondant.
Notez également qu'à moins que j'utilise la set_weapon()méthode sur une instance de personnage construite (c'est-à-dire pour changer d'arme au milieu d'une bataille), la classe de personnage serait inutile, car tout aurait pu être fait avec les armes seules. Bien sûr, je sais que ce n'est qu'un code de démonstration de modèle, mais je voulais quand même le souligner ...
En prime, voici quelque chose (pas seulement) pour le roi :) Notez également à quel point la composition est à nouveau préférée à l'héritage pour offrir de la flexibilité.
class DoubleWeapon:
def __init__(self, left, right):
self.left = left
self.right = right
def use(self):
self.left.use()
self.right.use()
king.set_weapon(DoubleWeapon(Knife(), Sword()))
king.fight()
```