From 90149cb7be7ad0b3bfb2cb3530c663fc489216c6 Mon Sep 17 00:00:00 2001 From: Ruidy Date: Sat, 3 Oct 2020 20:44:57 +0200 Subject: [PATCH] add code example --- behavioral/memento/caretaker.py | 34 +++++++++++++++++ behavioral/memento/concrete_memento.py | 25 +++++++++++++ behavioral/memento/main.py | 20 ++++++++++ behavioral/memento/memento.py | 21 +++++++++++ behavioral/memento/originator.py | 51 ++++++++++++++++++++++++++ 5 files changed, 151 insertions(+) create mode 100644 behavioral/memento/caretaker.py create mode 100644 behavioral/memento/concrete_memento.py create mode 100644 behavioral/memento/main.py create mode 100644 behavioral/memento/memento.py create mode 100644 behavioral/memento/originator.py diff --git a/behavioral/memento/caretaker.py b/behavioral/memento/caretaker.py new file mode 100644 index 0000000..1f603cf --- /dev/null +++ b/behavioral/memento/caretaker.py @@ -0,0 +1,34 @@ +from behavioral.memento.originator import Originator + + +class Caretaker: + """ + The Caretaker doesn't depend on the Concrete Memento class. Therefore, it + doesn't have access to the originator's state, stored inside the memento. It + works with all mementos via the base Memento interface. + """ + + def __init__(self, originator: Originator) -> None: + self._memento = [] + self._originator = originator + + def backup(self) -> None: + print("\nCaretaker: Saving Originator's state...") + self._memento.append(self._originator.save()) + + def undo(self) -> None: + if not self._memento: + return + + memento = self._memento.pop() + print(f"Caretaker: Restoring state to: {memento.get_name()}") + + try: + self._originator.restore(memento) + except Exception: + self.undo() + + def show_history(self) -> None: + print("Caretaker: Here's the list of mementos:") + for memento in self._memento: + print(memento.get_name()) diff --git a/behavioral/memento/concrete_memento.py b/behavioral/memento/concrete_memento.py new file mode 100644 index 0000000..c7daba7 --- /dev/null +++ b/behavioral/memento/concrete_memento.py @@ -0,0 +1,25 @@ +from datetime import datetime + +from behavioral.memento.memento import Memento + + +class ConcreteMemento(Memento): + + def __init__(self, state: str) -> None: + self._state = state + self._date = str(datetime.now())[:19] + + def get_state(self) -> str: + """ + The Originator uses this method when restoring its state. + """ + return self._state + + def get_name(self) -> str: + """ + The rest of the methods are used by the Caretaker to display metadata. + """ + return f"{self._date} / ({self._state[0:9]}...)" + + def get_date(self) -> str: + return self._date diff --git a/behavioral/memento/main.py b/behavioral/memento/main.py new file mode 100644 index 0000000..5feb5c7 --- /dev/null +++ b/behavioral/memento/main.py @@ -0,0 +1,20 @@ +from behavioral.memento.caretaker import Caretaker +from behavioral.memento.originator import Originator + +originator = Originator("Super-duper-super-puper-super.") +caretaker = Caretaker(originator) + +caretaker.backup() +originator.do_something() + +caretaker.backup() +originator.do_something() + +print() +caretaker.show_history() + +print("\nClient: Now, let's rollback!\n") +caretaker.undo() + +print("\nClient: Once more!\n") +caretaker.undo() diff --git a/behavioral/memento/memento.py b/behavioral/memento/memento.py new file mode 100644 index 0000000..c1b8853 --- /dev/null +++ b/behavioral/memento/memento.py @@ -0,0 +1,21 @@ +from abc import ABC, abstractmethod + + +class Memento(ABC): + """ + The Memento interface provides a way to retrieve the memento's metadata, + such as creation date or name. However, it doesn't expose the Originator's + state. + """ + + @abstractmethod + def get_state(self) -> str: + pass + + @abstractmethod + def get_name(self) -> str: + pass + + @abstractmethod + def get_date(self) -> str: + pass diff --git a/behavioral/memento/originator.py b/behavioral/memento/originator.py new file mode 100644 index 0000000..1e1e2b7 --- /dev/null +++ b/behavioral/memento/originator.py @@ -0,0 +1,51 @@ +from random import sample +from string import ascii_letters + +from behavioral.memento.concrete_memento import ConcreteMemento +from behavioral.memento.memento import Memento + + +class Originator: + """ + The Originator holds some important state that may change over time. It also + defines a method for saving the state inside a memento and another method + for restoring the state from it. + """ + + """ + For the sake of simplicity, the originator's state is stored inside a single + variable. + """ + _state = None + + def __init__(self, state: str) -> None: + self._state = state + print(f"Originator: My initial state is: {self._state}") + + def do_something(self) -> None: + """ + The Originator's business logic may affect its internal state. + Therefore, the client should backup the state before launching methods + of the business logic via the save() method. + """ + print("Originator: I'm doing something important.") + self._state = self._generate_random_string(30) + print(f"Originator: and my state has changed to: {self._state}") + + def _generate_random_string(self, length: int) -> str: + return "".join(sample(ascii_letters, length)) + + def save(self) -> Memento: + """ + Saves the current state inside a memento. + """ + return ConcreteMemento(self._state) + + def restore(self, memento: Memento) -> None: + """ + Restores the Originator's state from a memento object. + """ + + self._state = memento.get_state() + + print(f"Originator: My state has changed to: {self._state}")