python design patterns Chapter 5 - behavioral patterns

1. Responsibility chain model

        The content of the responsibility chain pattern: multiple objects have the opportunity to process the request, so as to avoid the coupling relationship between the sender and receiver of the request. Connect these objects into a chain and pass the request along the chain until an object processes it. The roles of responsibility chain include abstract handler, concrete handler and client.

from abc import ABCMeta, abstractmethod

# Abstract processor
class Handler(metaclass=ABCMeta):
    @abstractmethod
    def handle_leave(self, day):
        pass

# Specific handler
class GeneralManager(Handler):
    def handle_leave(self, day):
        if day <= 30:
            print('Leave granted by the general manager%d' % day)
        else:
            print('You can resign!')

# Specific handler
class DepartmentManager(Handler):
    def __init__(self):
        self.next = GeneralManager()

    def handle_leave(self, day):
        if day <= 7:
            print('Leave granted by project director%d' % day)
        else:
            print('Insufficient authority of Department Manager')
            self.next.handle_leave(day)

# Specific handler
class ProjectDirector(Handler):
    def __init__(self):
        self.next = DepartmentManager()

    def handle_leave(self, day):
        if day <= 3:
            print('Leave granted by project director%d' % day)
        else:
            print('Insufficient authority of project director')
            self.next.handle_leave(day)

day = 20
p = ProjectDirector()
p.handle_leave(day)
"""
Insufficient authority of project director
 Insufficient authority of Department Manager
 Leave granted by the general manager 20
"""

        Usage scenario: there are multiple objects that can process a request, and the processing of which object is determined by the runtime; Submit a request to one of multiple objects without specifying the recipient. The advantage is to reduce the degree of coupling. One object does not need to know which other object handles its request.

2. Observer mode

        Observer mode is widely used, also known as "publish subscribe" mode. It is used to define a one to many dependency between objects. When the state of an object changes, all objects that depend on it are notified and updated automatically. The roles of observer pattern are: abstract topic, concrete topic (publisher), abstract observer and concrete observer (subscriber).

from abc import ABCMeta, abstractmethod

# Abstract subscriber
class Observer(metaclass=ABCMeta):
    @abstractmethod
    def update(self, notice):
        """
        :param notice: Notice Object of class
        :return:
        """
        pass

# Abstract Publisher: it can be an interface. Subclasses do not need to be implemented, so there is no need to define abstract methods!
class Notice:
    def __init__(self):
        self.observers = []

    def attach(self, obs):
        self.observers.append(obs)

    def detach(self, obs):
        self.observers.remove(obs)

    def notify(self):
        """
        Push
        :return:
        """
        for obs in self.observers:
            obs.update(self)

# Specific publisher
class StaffNotice(Notice):
    def __init__(self, company_info):
        super().__init__()  # Call the parent class object to declare the observers property
        self.__company_info = company_info

    @property
    def company_info(self):
        return self.__company_info

    @company_info.setter
    def company_info(self, info):
        self.__company_info = info
        self.notify()

# Specific subscribers
class Staff(Observer):
    def __init__(self):
        self.company_info = None

    def update(self, notice):
        self.company_info = notice.company_info

staff_notice = StaffNotice('Initialize company information')
staff1 = Staff()
staff2 = Staff()
staff_notice.attach(staff1)
staff_notice.attach(staff2)
# print(staff1.company_info) None
# print(staff2.company_info) None
staff_notice.company_info = 'Holiday notice!'
print(staff1.company_info)
print(staff2.company_info)
staff_notice.detach(staff2)
staff_notice.company_info = 'Meeting tomorrow!'
print(staff1.company_info)
print(staff2.company_info)
"""
Holiday notice!
Holiday notice!
Meeting tomorrow!
Holiday notice!
"""

        Usage scenario: when an abstract model has two aspects, one of which depends on the other. Encapsulating the two in independent objects so that they can be changed and reused independently; When changing one object requires changing other objects at the same time, we don't know how many objects need to be changed; When an object must notify other objects, it cannot assume who the other objects are. In other words, you don't want these objects to be tightly coupled. Advantages: the abstract coupling between the target and the observer is minimal; Support broadcast communication.

3. Strategy mode

        Define algorithms, encapsulate them, and make them interchangeable. This pattern allows the algorithm to vary independently of the customers using it. Roles include: Abstract strategy, concrete strategy and context.

from abc import abstractmethod, ABCMeta
from datetime import datetime

# Abstract strategy
class Strategy(metaclass=ABCMeta):
    @abstractmethod
    def execute(self, data):
        pass

# Specific strategies
class FastStrategy(Strategy):
    def execute(self, data):
        print("Use faster policy processing%s" % data)

# Specific strategies
class SlowStrategy(Strategy):
    def execute(self, data):
        print("Use slower policy processing%s" % data)

# context
class Context:
    def __init__(self, strategy, data):
        self.data = data
        self.strategy = strategy
        # You can define things that users don't know
        self.date = datetime.now()

    def set_strategy(self, strategy):
        self.strategy = strategy

    def do_strategy(self):
        self.strategy.execute(self.data)

data = "Hello!"
# Use faster policy processing
fast_strategy = FastStrategy()
context = Context(fast_strategy, data)
context.do_strategy()
# Use slower policy processing
slow_strategy = SlowStrategy()
context = Context(slow_strategy, data)
context.do_strategy()
"""
Use faster policy processing Hello!
Use slower policy processing Hello!
"""

        Advantages: some reusable algorithms and behaviors are defined; Some conditional statements are eliminated; Different implementations of the same behavior can be provided; Disadvantages: customers must understand different strategies.

4. Template method mode

        Content: define the algorithm skeleton in an operation and delay some steps to subclasses. Template method allows subclasses to redefine some specific steps of an algorithm without changing the structure of the algorithm. Using template method requires two roles: abstract class and concrete class. Abstract classes are used to define abstract classes (hook operations) and implement a template method as the skeleton of the algorithm. The role of concrete classes is to realize atomic operations.

from abc import ABCMeta, abstractmethod
from time import sleep

# abstract class
class Window(metaclass=ABCMeta):
    @abstractmethod
    def start(self):  # Atomic operation / hook operation
        pass

    @abstractmethod
    def repaint(self):  # Atomic operation / hook operation
        pass

    @abstractmethod
    def stop(self):  # Atomic operation / hook operation
        pass

    def run(self):
        """
        Template method(Specific method),This big logic doesn't need to be written by yourself
        :return:
        """
        self.start()
        while True:
            try:
                self.repaint()
                sleep(1)
            except KeyboardInterrupt:
                break
        self.stop()

# concrete class
class MyWindow(Window):
    def __init__(self, msg):
        self.msg = msg

    def start(self):
        print('The window starts running!')

    def stop(self):
        print('Window stop!')

    def repaint(self):
        print(self.msg)

MyWindow("Hello...").run()

        Applicable scenarios of template method: implement the invariant part of an algorithm at one time, and the public behaviors in each subclass should be extracted and concentrated in a common parent class to avoid code duplication; Control subclass extension.

Tags: Python Design Pattern

Posted on Tue, 12 Oct 2021 02:48:51 -0400 by aarons123