@Lenma : c’est en effet assez courant d’utiliser des classes abstraites avec strategy
@Brice: la strategy peut être un objet complet avec plusieurs methodes.
]]>Quelqu’un voit des avantages ou inconvénients de cette manière de mettre en place cette stratégie? Ça me semble assez élégant en tout cas…
]]>Petit question pour pousser ce design pattern un peu plus loin : je sais que les exemples doivent êtres courts pour aller à l’essentiel mais dans ce genre de stratégie ne serait il pas approprié de créer une classe abstraite pour MaStrategie avec toutes les méthodes requise par la classe MonObjet et tester que MaStrategie ou MonAutreStrategie (ou n’importe quoi d’autre) hérite bien de cette classe abstraite.
Cela permettrai de fournir une API plus sûr pour quiconque voudrait écrire sa propre classe stratégie.
J’ai écris un exemple pour illustrer mon propos mais je suis dans l’embarra car je ne trouve pas la balise approprier pour afficher le code python proprement dans les commentaires, du coup c’est pas terriblement fonctionnel vu que des indentations disparaissent…
from abc import ABCMeta, abstractmethod # Classe abstraite qui ne peut être instanciée class MaStrategieAbstraite(object): # Metaclass associé au décorateur abstractmethod __metaclass__ = ABCMeta def __init__(self, parent): super(MaStrategieAbstraite, self).__init__() # ... # Décorateur qui oblige a réécrire la méthode dans les classes filles. # Il ne faut pas oublier de définir la méta-classe ABCMeta pour que ça # fonctionne. @abstractmethod def foo(self, arg1, arg2): """ Cette méthode prend deux entiers et revois la somme des deux. """ pass class MaStrategie(MaStrategieAbstraite): # Si cette méthode n'est pas défini il sera impossible de créer des # instances de cette classe. def foo(self, arg1, arg2): return arg1 + arg2 class MonObjet: # On appelle souvent cet attribut "strategy_class" ou "strategy_factory" strategie_par_default = MaStrategie def __init__(self, strategie=None): strategie = strategie if strategie else self.strategie_par_default # On test le lien de parenté pour être sûr qu'il n'y aura pas de # problème de méthodes manquantes par la suite if not issubclass(strategie, MaStrategieAbstraite): raise TypeError("C'est pour ton bien mon enfant !") self.strategie = strategie(self) def addition(self, a, b): return self.strategie.foo(a, b) # On pourrais aussi hériter de `MaStrategie` si il y a plusieurs # methodes abstraites et qu'on veut n'en redéfinir qu'une seule. class MonAutreStrategie(MaStrategieAbstraite): def foo(self, arg1, arg2): print('%i + %i = %i' % (arg1, arg2, arg1+arg2)) return arg1 + arg2 class MonSousObjet(MonObjet): # Et boom, overriding de la stratégie par la classe enfant en une ligne. # Django fait ça par exemple avec les classes based views et l'attribut # model strategie_par_default = MonAutreStrategie # Exemples en action # ------------------ mon_objet = MonObjet() mon_objet.addition(1, 2) # Revois 3 mon_sous_objet = MonSousObjet() mon_sous_objet.addition(1, 2) # affiche '1 + 2 = 3' et revois 3 class MauvaiseExampleDeStrategie(MaStrategieAbstraite): pass class AutreMauvaiseExampleDeStrategie(object): pass # Lève une exception (TypeError) puique la méthode foo n'a pas été redéfinie mon_objet2 = MonObjet(strategie=MauvaiseExampleDeStrategie) # Lève une exception (TypeError) puique la strategie n'hérite pas de `MaStrategieAbstraite` mon_objet3 = MonObjet(strategie=MauvaiseExampleDeStrategie)]]>
Ou je dis une connerie ?
]]>Par contre, dans l’avant dernier exemple, ce ne serait pas par hasard
self.strategie = strategie(self) if strategie else self.strategie_par_default(self)
]]>POO un peu avancé -> avancée
et créer des stratégies à la volées -> volée
C’est rare que ça arrive, est c’est vraiment -> et
]]>