Dans ce tutoriel, nous apprendrons pourquoi et comment utiliser le design pattern Memento.
Nous le définirons puis l’analyserons au travers d’un exemple pratique avec Python.
Pré-requis : Avoir les bases en programmation orientée objet => Tutoriel OpenClassroom
Définition :
Mémento : Patron de conception qui permet de renvoyer un objet à un état précèdent sans violer le principe d’encapsulation utilisé par 2 objets. Voir source
Exemple pratique :
On souhaite incrémenter un nombre, si durant le déroulement une erreur survient la valeur précèdente sera restaurée.
Restaurer à un état précèdent
Pour restaurer notre nombre à un état précèdent, nous créeons la méthode memento.
from copy import copy, deepcopy def memento(obj, deep=False): state = deepcopy(obj.__dict__) if deep else copy(obj.__dict__) def restore(): obj.__dict__.clear() obj.__dict__.update(state)
Le gardien de transaction
Le gardien de transaction sauvegarde une nouvelle valeur avec commit et restaure une ancienne avec rollback.
class Transaction(object): deep = False states = [] def commit(self): self.states = [memento(target, self.deep) for target in self.targets] def __init__(self, deep, *targets): self.deep = deep self.targets = targets self.commit() def rollback(self): print("retour en arrière") print(self.states) for state in self.states: print(state)
Restauration automatique de la valeur sauvegardé si echec
Une méthode décorée avec Transactional retrouvera son état précèdent si une exception est levée.
class Transactional(object): def __init__(self, method): self.method = method def __get__(self, obj, T): def transaction(*args, **kwargs): state = memento(obj) try: return self.method(obj, *args, **kwargs) except Exception as e: state() raise e return transaction
Nombre à incrémenter
La méthode do_stuff ci-dessous est décoré avec la classe Transactional. Si une erreur apparait dans do_stuff, l’état de value sera restauré.
class NumObj(object): def __init__(self, method): self.method = method def __repr__(self): return '<%s: %r>' % (self.__class__.__name__, self.value) def increment(self): self.value += 1 @Transactional def do_stuff(self): self.value = '1111' self.increment()
Test du comportement de memento
if __name__ == '__main__': num_obj = NumbObj(-1) a_transaction = Transaction(True, num_obj) try: for i in range(3): num_obj.increment() print(num_obj) a_transaction.commit() print('-- committed') for i in range(3): num_obj.increment() print(num_obj) num_obj.value += 'x' print(num_obj) a_transaction.commit() print(num_obj) except Exception as e: a_transaction.rollback() print('-- rolled back --') print('-- now doing stuff --') try: num_obj.do_stuff() except Exception as e: print('-> doing stuff failed') import sys import traceback print(num_obj)
Conclusion
Ainsi s’achève ce tutoriel, si vous avez des remarques ou des questions, vous pouvez les ajouter en commentaire.