Memento, utiliser ce patron de conception avec Python

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

upidev patron de conception memento python

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.

0

Laisser un commentaire