current position:Home>Implementation of top-level design pattern in Python

Implementation of top-level design pattern in Python

2021-08-22 05:45:31 PythonCN

In software development , Design patterns are solutions to common problems in a specific context , These schemes have been verified .

Their main goal is to show us a good way to program and explain why other options don't work .

Use common design patterns , You can :

  • Speed up the development process ;
  • Reduce the number of lines of code ;
  • Make sure your code is well designed ;
  • Foresee the problems caused by small problems in the future .

Design patterns can significantly improve the lives of software developers , Regardless of him ( she ) What programming language is used .

I interviewed Founder and CTO of 、 Have 9 More than years of experience Python Developers and software architects Roman Latyshenko About his top design patterns .

I mainly use Python/Django, So here is what I use every day at work Python List of design patterns in .

Behavior type

Iterator pattern

Iterators allow you to iterate over the elements of a collection without exposing internal details .

Use scenarios . Most of the time , I use it to provide a standard way to traverse a collection .

Clean client code ( Principle of single responsibility ).
You can introduce iterators into the collection without changing the client code ( to open up / Closed principle ).
Each iteration object has its own iteration state , So you can postpone and continue iterations .
Using iterators for simple collections can overload the application .


Code example

from __future__ import annotations
from import Iterable, Iterator
from typing import Any, List

class AlphabeticalOrderIterator(Iterator):
    _position: int = None
    _reverse: bool = False

    def __init__(self, collection: WordsCollection, 
                 reverse: bool = False):
        self._collection = collection
        self._reverse = reverse
        self._position = -1 if reverse else 0

    def __next__(self):
            value = self._collection[self._position]
            self._position += -1 if self._reverse else 1
        except IndexError:
            raise StopIteration()
        return value

class WordsCollection(Iterable):
    def __init__(self, collection: List[Any] = []):
        self._collection = collection

    def __iter__(self) -> AlphabeticalOrderIterator:
        return AlphabeticalOrderIterator(self._collection)

    def get_reverse_iterator(self) -> AlphabeticalOrderIterator:
        return AlphabeticalOrderIterator(self._collection, True)

    def add_item(self, item: Any):

if __name__ == "__main__":
    collection = WordsCollection()

    print("Straight traversal:")

    print("Reverse traversal:")

The state pattern

State patterns help objects change their behavior when their internal state changes .

Use scenarios . State mode helps me

  • Change a large number of object states .
  • Reduce the number of lines of repetitive code in similar transitions and states .
  • Avoid a large number of conditions .

Follow the principle of single responsibility : Separate the classes of code related to different states .
Adding a new state does not change the context or state of the class ( open / Closed principle ).
With little change in the state machine , There may be too many usage States .


Sample code

from __future__ import annotations
from abc import ABC, abstractmethod

class Context(ABC):
    _state = None
    def __init__(self, state: State):
    def transition_to(self, state: State):
        print(f"Context: Transition to {type(state).__name__}")
        self._state = state
        self._state.context = self
    def request1(self):
    def request2(self):
class State(ABC):
    def context(self) -> Context:
        return self._context
    def context(self, context: Context):
        self._context = context
    def handle1(self):
    def handle2(self):
class ConcreteStateA(State):
    def handle1(self):
        print("ConcreteStateA handles request1.")
        print("ConcreteStateA wants to change the state of the context.")
    def handle2(self):
        print("ConcreteStateA handles request2.")
class ConcreteStateB(State):
    def handle1(self):
        print("ConcreteStateB handles request1.")
    def handle2(self):
        print("ConcreteStateB handles request2.")
        print("ConcreteStateB wants to change the state of the context.")
if __name__ == "__main__":
    context = Context(ConcreteStateA())

Observer mode

Observers will inform them of events that occur in other objects they observe , Without having to couple to their classes .

Use scenarios . Every time I need to add a subscription mechanism to make objects subscribe / When unsubscribing notifications of events that occur for a particular publisher class , I use observer mode .

A good example is simply subscribing to news from any online magazine , You can usually choose your field of interest ( science 、 Digital technology, etc ). perhaps , Of e-commerce platform “ Let me know when you have goods ” Buttons are another example .

You don't have to change the publisher's code to add the subscriber's class .
Subscribers receive notifications in random order .


Sample code

from __future__ import annotations
from abc import ABC, abstractmethod
from random import randrange
from typing import List

class Subject(ABC):
    def attach(self, observer: Observer):
    def detach(self, observer: Observer):
    def notify(self):
class ConcreteSubject(Subject):
    _state: int = None
    _observers: List[Observer] = []
    def attach(self, observer: Observer):
        print("Subject: Attached an observer.")

    def detach(self, observer: Observer):
    def notify(self):
        print("Subject: Notifying observers...")
        for observer in self._observers:
    def some_business_logic(self):
        print("Subject: I'm doing something important.")
        self._state = randrange(0, 10)
        print(f"Subject: My state has just changed to: {self._state}")
class Observer(ABC):
    def update(self, subject: Subject):       
class ConcreteObserverA(Observer):
    def update(self, subject: Subject):
        if subject._state < 3:
            print("ConcreteObserverA: Reacted to the event")
class ConcreteObserverB(Observer):
    def update(self, subject: Subject):
        if subject._state == 0 or subject._state >= 2:
            print("ConcreteObserverB: Reacted to the event")
if __name__ == "__main__":    
    subject = ConcreteSubject()
    observer_a = ConcreteObserverA()
    observer_b = ConcreteObserverB()

Structural type

Appearance mode

The facade pattern provides a simplified but limited interface to reduce the complexity of the application . Appearance mode can “ shielding ” A complex subsystem with multiple moving parts .

Use scenarios . I created the appearance pattern class , In case I have to use complex libraries and API and ( or ) I only need some of their functions .

System complexity and code separation
Use appearance mode , You can create a god object .


Sample code

class Addition:
    def __init__(self, field1: int, field2: int):
        self.field1 = field1
        self.field2 = field2
    def get_result(self):
        return self.field1 + self.field2
class Multiplication:
    def __init__(self, field1: int, field2: int):
        self.field1 = field1
        self.field2 = field2
    def get_result(self):
        return self.field1 * self.field2
class Subtraction:
    def __init__(self, field1: int, field2: int):
        self.field1 = field1
        self.field2 = field2
    def get_result(self):
        return self.field1 - self.field2
class Facade:
    def make_addition(*args) -> Addition:
        return Addition(*args)
    def make_multiplication(*args) -> Multiplication:
        return Multiplication(*args)
    def make_subtraction(*args) -> Subtraction:
        return Subtraction(*args)
if __name__ == "__main__":
    addition_obj = Facade.make_addition(5, 5)
    multiplication_obj = Facade.make_multiplication(5, 2)
    subtraction_obj = Facade.make_subtraction(15, 5)

Decorator mode

Decorators attach new behaviors to objects without modifying their structure .

This pattern generates a decorator class to wrap the original class and add new functionality .

Use scenarios . Every time I need to add additional behavior to an object without entering the code , I can use decorator mode .

Change object behavior without creating subclasses .
You can combine multiple behaviors by wrapping an object into multiple decorators .
A particular decorator is difficult to remove from the wrapper stack .


Sample code

class my_decorator:
    def __init__(self, func):
        print("inside my_decorator.__init__()")
        func() # Prove that function definition has completed
    def __call__(self):
        print("inside my_decorator.__call__()")
def my_function():
    print("inside my_function()")
if __name__ == "__main__":    

Adapter pattern

The adapter pattern serves as an intermediate class to connect functions of independent or incompatible interfaces .

Use scenarios . Set collaboration between interfaces , I use adapter mode to solve the problem of format incompatibility .

for example , The adapter can help to XML Data format converted to JSON For further analysis .

Allows separation of interfaces from business logic .
Adding a new adapter won't break the client's code
Increase code complexity


Sample code

class Target:
    def request(self):
        return "Target: The default target's behavior."
class Adaptee:
    def specific_request(self):
        return ".eetpadA eht fo roivaheb laicepS"
class Adapter(Target, Adaptee):
    def request(self):
        return f"Adapter: (TRANSLATED) {self.specific_request()[::-1]}"
def client_code(target: "Target"):
if __name__ == "__main__":
    print("Client: I can work just fine with the Target objects:")
    target = Target()
    adaptee = Adaptee()
    print("Client: The Adaptee class has a weird interface. "
          "See, I don't understand it:")
    print(f"Adaptee: {adaptee.specific_request()}")
    print("Client: But I can work with it via the Adapter:")
    adapter = Adapter()

Creation type

The singleton pattern

The singleton pattern limits a class to having multiple instances , And ensure that the global access point of the instance .

Use scenarios . Singleton mode helps me

  • Managing shared resources : That is, a single database shared by multiple parts of the application 、 File manager or printer spooler .
  • Store global status ( Help file path 、 User language 、 Application path, etc ).
  • Create a simple logger.

Class has only one instance
It's hard to unit test code , Because most test frameworks are created mock Object using inheritance .


Code example

class Singleton:
    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance
if __name__ == "__main__":
    s = Singleton()
    print("Object created:", s)
    s1 = Singleton()
    print("Object created:", s1)

When to use Python Design mode of ?

When you need to work for multiple API Option provides a unified interface , Appearance mode is very useful . for example , You should integrate a payment system into your application , Leave the possibility of changing it . under these circumstances , You can use appearance mode , You just need to create a new look , Without rewriting the entire application .

If API Completely different , The problem here will arise , Because it's not easy to design a common interface for appearance patterns .

State is used to manage multiple independent components of an application , The premise is that the initial architecture means their independence . therefore , It may be a good idea to create a separate module for state management and use the observer pattern .

Due to the built-in support for decorators , Decorators are probably the most commonly used Python Pattern . for example , Decorators provide a convenient and explicit way to use some libraries , And create more and more rich opportunities for application design and management . This pattern also ensures a wide range of possibilities for function combinations , And found new opportunities for functional programming .

The adapter is suitable for processing a large number of data in different formats . This pattern allows one algorithm to be used for each data format instead of multiple algorithms .

Iterators have similar benefits , So they can be used together . Besides , One of the iterator variants called generators ( A long time ago in Python Introduction in ) Allow more efficient use of memory , It is very valuable for some types of projects when dealing with large amounts of data .

Last , The importance of the singleton pattern cannot be underestimated : Database connection 、API、 file …… These are the times when developers should know how to avoid mistakes in the process . Singleton mode can do well here , Not to mention the possibility of using the same instance every time instead of copying it to reduce memory consumption .

Implementation of Top Design Patterns in Python

copyright notice
author[PythonCN],Please bring the original link to reprint, thank you.

Random recommended