refactoring modello di strategia python per utilizzare la classe base astratta
Mi sono imbattuto in questa implementazione del modello di strategia
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()
Sarebbe corretto rifattorizzarlo nel modo seguente, usando 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()
Risposte
Solo le classi Character e WeaponBehaviour sono effettivamente utili e nessuna delle due deve ereditare nulla. Il resto può essere solo funzioni di fabbrica, perché differiscono solo i costruttori. Se un costruttore di classi fa qualsiasi cosa tranne assegnare argomenti alle proprietà, probabilmente è sbagliato.
Il modello strategico si basa sulla composizione piuttosto che sull'ereditarietà.
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()
Si noti che ho rimosso la behaviorparte dei nomi in quanto sembrava un po 'inutile.
L'ho anche rinominato use_weapon()solo use()perché viene richiamato su una weaponvariabile, e quindi sembrava ridondante.
Notate anche che, a meno che non usassi il set_weapon()metodo su un'istanza del personaggio costruita (cioè per cambiare arma nel bel mezzo della battaglia), la classe del personaggio sarebbe stata inutile, perché tutto avrebbe potuto essere fatto con le sole armi. Ovviamente so che questo è solo un codice dimostrativo di pattern, ma volevo sottolineare comunque ..
Come bonus, ecco qualcosa (non solo) per il re :) Notate anche come ancora una volta la composizione sia preferita all'ereditarietà per fornire flessibilità.
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()
```