Twenty-three design patterns

Three common basic design patterns

  1. Creation mode

    Provide instantiation methods and corresponding creation methods for suitable situations.

  2. Structured model

    Used to deal with the relationship between entities, so that these entities can work together better.

  3. Behavior mode

    It is used for communication between different entities and provides an easier and more flexible communication method for communication between entities.

Detailed mode introduction

Creation type

1. Factory Method

Factory Method

  1. effect

    Define an interface for creating objects and let subclasses decide which class should be instantiated. Factory Method delays the instantiation of a class to its subclasses.

  2. Applicable scene

    • When a class does not know the class of the object it must create.
    • When a class wants its subclasses to specify the objects it creates.
  3. Sample Demo

    class ChinaGetter(object):
        def __init__(self):
            self.trans = dict(dog='小狗',cat='小猫')
    
        def get(self, msg_id):
            try:
                return self.trans[msg_id]
            except KeyError:
                return str(msg_id)
    
    class EnglishGetter(object):
        def get(self, msg_id):
            return str(msg_id)
    
    def get_localizer(language='English'):
        languages = dict(English=EnglishGetter, China=ChinaGetter)
        return languages[language]()
    
    # create localizer
    e, g = get_localizer('English'), get_localizer('China')
    for text in "A cat sits on the desk".split():
        print(e.get(text), g.get(text))
    

2. Abstract Factory

  1. effect

    Provide an interface for creating related or interdependent objects without specifying their specific classes.

  2. Applicable scene

    • When a system is independent from the creation, combination and presentation of its products
    • When a system requires one of multiple product families to configure
    • When emphasizing the design of a series of related product objects for joint use
    • When providing a product class library, but only want to display their interfaces instead of implementation.
  3. Sample Demo

    import random
    
    class Cat(object):
        def speak(self):
            return 'meow'
    
        def __str__(self):
            return 'Cat'
    
    class Dog(object):
        def speak(self):
            return 'woolf'
    
        def __str__(self):
            return 'Dog'
    
    class CatFactory(object):
        def get_pet(self):
            return Cat()
    
        def get_food(self):
            return 'Cat food'
    
    class DogFactory(object):
        def get_pet(self):
            return Dog()
    
        def get_food(self):
            return 'Dog food'
    
    class PetShop(object):
        def __init__(self, animal_factory=None):
            self.factory = animal_factory
    
        def show_pet(self):
            pet = self.factory.get_pet()
            print('This is a lovely ', str(pet))
            print('pet speaks ', pet.speak())
            print('pet eats ', pet.get_food())
    
    def get_factory():
        return random.choice([DogFactory, CatFactory])
    

3. Builder

Factory Method

  1. effect

    Separate the construction of a complex object from its representation so that the same construction process can create different representations.

  2. Applicable scene

    • When the creation of a complex object is independent of the sub-components that make up the object, and how the sub-components are assembled
    • Allow different objects to be created by different construction processes
  3. Sample Demo

    class Director(object):
       def __init__(self):
           self.builder = None
    
       def construct_building(self):
           self.builder.new_building()
           self.builder.build_floor()
           self.builder.build_size()
       
       def get_building(self):
           return self.builder.new_building
       
    class Builder(object):
       def __init__(self):
           self.building = None
       
       def new_building(self):
           self.building = Building()
    
       
    class BuilderHouse(Builder):
       def build_floor(self):
           self.building.floor = "One"
       
       def build_size(self):
           self.building.size = "Big"
    
    class BuilderFlat(Builder):
       def build_floor(self):
           self.building.floor = "Two"
    
       def build_size(self):
           self.building.size = "Small"   
    
    class Building(object):
       def __init__(self):
           self.floor = None
           self.size = None
    
       def __repr__(self):
           return "Floor : %s | Size: %s" % (self.floor, self.size)
    
    if __name__ == '__main__':
       director = Director()
       director.builder = BuilderHouse()
       director.construct_building()
       building = director.get_building()
       print(building)
       director.builder = BuilderFlat()
       director.construct_building()
       building = director.get_building()
       print(building)   
    

4. Prototype (prototype)

Factory Method

  1. effect

    Use prototype instances to create objects, and copy these prototypes to create new objects.

  2. Applicable scene

    • Dynamic loading, that is, when the class to be instantiated is specified at runtime
    • To avoid creating a factory class level parallel to the product class level
    • When an instance of a class can only have a combination of several different states

    It is recommended that a corresponding number of prototypes, and clone them, be more convenient than instantiating manually with the appropriate state each time.

  3. Sample Demo

    import copy
    
    class Prototype:
        def __init__(self):
            self._objects = {}
    
        def register_object(self, name, obj):
            self._objects[name] = obj
    
        def unregister_object(self, name):
            del self._objects[name]
    
        def clone(self, name, **attr):
            obj = copy.deepcopy(self._objects.get(name))
            obj.__dict__.update(attr)
            return obj
    
    def main():
        class A:
            def __str__(self):
                return "I"'m A.'
        
        a = A()
        prototype = Prototype()
        prototype.register_object('a', a)
        b = prototype.clone('a', a=1, b=2, c=3)
    
        print(a)
        print(b.a, b.b, b.c)
    
    if __name__ == '__main__':
        main()
    

5. Singleton (single case)

Factory Method

  1. effect

    Ensure that only one instance of a class exists

  2. Applicable scene

    • When the class allows only one instance, and the client can access it from a global variable
    • When this unique instance can be extended by subclasses, and the client can use an extended instance without changing the code
  3. Sample Demo

    class Singleton(object):
        def __new__(cls, *args, **kwargs):
            if not hasattr(cls, '_instance'):
                org = super(Singleton, cls)
                cls._instance = org.__new__(cls, *args, **kwargs)
            return cls._instance
    
    class SingleSpam(Singleton):
        def __init__(self, s):
            self.s = s
        
        def __str__(self):
            return self.s
    
    if __name__ == '__main__':
        spam = SingleSpam('spam')
        print(id(spam), spam)
    
        spa = SingleSpam('spa')
        print(id(spa), spa)
        print(id(spam), spam)
    

Structural type

6. Adapter Class / Object (Adapter)

Factory Method

  1. effect

    Convert the interface of one class to another interface that the client wants. The Adapter mode enables classes that could not work together because of incompatible interfaces to work together again.

  2. Applicable scene

    • Trying to use an existing class whose interface does not meet your needs
    • Trying to create a reusable class that can work with other unrelated or unpredictable classes
    • Trying to use existing subclasses, but cannot subclass each one to match their interface.
  3. Sample Demo

    import os
    
    class Dog(object):
        def __init__(self):
            self.name = "Dog"
    
        def bark(self):
            return "woof"
    
    class Cat(object):
        def __init__(self):
            self.name = "Cat"
    
        def meow(self):
            return "meow"
    
    class Human(object):
        def __init__(self):
            self.name = "Human"
    
        def speak(self):
            return "hello"
    
    class Car(object):
        def __init__(self):
            self.name = "Car"
    
        def make_noise(self, octane_level):
            return "vroom" % ("!" * octane_level)
    
    
    class Adapter(object):
        def __init__(self, obj, adapted_methods):
            self.obj = obj
            self.__dict__.update(adapted_methods)
    
        def __getattr__(self, item):
            return getattr(self.obj, attr)
    
    
    def main():
        objects = []
        dog = Dog()
        objects.append(Adapter(dog, dict(make_noise=dog.bark)))
        cat = Cat()
        objects.append(Adapter(cat, dict(make_noise=cat.meow)))
        human = Human()
        objects.append(Adapter(human, dict(make_noise=human.speak)))
        car = Car()
        car_noise = lambda: car.make_noise(3)
        objects.append(Adapter(car, dict(make_noise=car_noise)))
    
        for obj in objects:
            print("{0} said {1}".format(obj.name, obj.make_noise()))
    
    if __name__ == "__main__":
        main()
    

7. Bridge

Factory Method

  1. effect

    Separate the abstract part and its realization part, so that they can change independently

  2. Applicable scene

    • Try to decouple the abstraction and the realization part.
    • The abstraction of the class and its implementation can be expanded by generating subclasses.
    • Modifying an abstract implementation part should not affect the client, that is, the client does not need to be recompiled.
  3. Sample Demo

    # ConcreteImplementor
    class DrawingApi1(object):
    
        def draw_circle(self, x, y, radius):
    
            print("Api_1.circle at {}:{} radius {}".format(x, y, radius))
    
    # ConcreteImplementor
    class DrawingApi2(object):
    
        def draw_circle(self, x, y, radius):
    
            print("Api_1.circle at {}:{} radius {}".format(x, y, radius))
    
    
    # Abstraction
    class CircleShape(object):
        def __init__(self, x, y, radius, drawing_api):
            self._x = x
            self._y = y
            self._radius = radius
            self._drawing_api = drawing_api
    
        # low level, i.e. implementation
        def draw(self):
            self._drawing_api.draw_circle(self._x, self._y, self._radius)
    
        # high level i.e. Abstraction
        def scale(self, pct):
            self._radius *= pct
    
    
    def main():
        shapes = (
            CircleShape(1, 2, 3, DrawingApi1()),
            CircleShape(5, 7, 11, DrawingApi2())
        )
        
        for shape in shapes:
            shape.scale(2.5)
            shape.draw()
    
    if __name__ == '__main__':
        main()
    

8. Composite

Composite

  1. effect

    Combining objects into a tree structure to represent a part-whole hierarchy makes client use of single objects and combined objects more consistent.

  2. Applicable scene

    • Need for part-whole hierarchy
    • I hope that the client ignores the difference between the combined object and the single object and uses the combined structure uniformly.
  3. Sample Demo

    # Abstraction
    class Component(object):
        def __init__(self, name):
            self._name = name
    
        def add(self, com):
            pass
    
        def display(self, depth):
            pass
    # Instance
    class Leaf(Component):
        def add(self, com):
            print("leaf can not add")
    
        def display(self, depth):
            name = "-" * depth
            name += self._name
            print(name)
    
    
    class Composite(Component):
        def __init__(self, name):
            super(Composite).__init__(name)
            self.entities = []
    
        def add(self, entity):
            self.entities.append(entity)
        
        def display(self, depth):
            name = "-" * depth
            name += self._name
            for entity in self.entities:
                entity.display(depth+2)
    
    
    if __name__ == "__main__":
        p = Composite("Wong")
        p.add(Leaf("Lee"))
        p.add(Leaf("Zhao"))
        p1 = Composite("Wu")
        p1.add(Leaf("San"))
        p.add(p1)
        p.display(1)
    

9. Decorator (decorator)

Decorator

  1. effect

    Add some extra features to an object dynamically. In terms of usage, the decorator pattern is more flexible than the way of generating subclasses.

  2. Applicable scene

    • Add responsibilities to individual objects in a dynamic and transparent manner without affecting other objects
    • Handling revocable duties
    • When the method of generating subclasses cannot be used for expansion:
      • There may be a large number of independent extensions. To support each combination, a large number of subclasses will be generated, and the number of subclasses will explode.
      • Class definitions may be hidden, or class definitions cannot be used to generate subclasses
  3. Sample Demo

    class Foo(object):
        def func1(self):
            print("original func1")
    
        def func2(self):
            print("original func2")
    
    class FooDecorator(object):
        def __init__(self, decorator):
            self._decorator = decorator
    
        def func1(self):
            print("decorated func1")
            self._decorator.func1()
    
        def __getattr__(self, item):
            return getattr(self._decorator, item)
    
    
    if __name__ == '__main__':
        foo = Foo()
        foo_decorator = FooDecorator(foo)
        foo_decorator.func1()
        foo_decorator.func2()
    

10. Facade (appearance)

Facade

  1. effect

    To provide a consistent interface for a set of interfaces of the subsystem, the Facade mode defines a high-level interface, making the subsystem easier to use.

  2. Applicable scene

    • When the subsystem becomes complicated due to continuous evolution, trying to provide a simple interface for the complex subsystem
    • When most patterns are used, more and smaller classes will be generated, trying to reuse subclasses or customize the subsystem
    • There is a great dependence between the client and the implementation of the abstract class. The introduction of Facade can separate the subsystem from the client and other subsystems, improving the independence and portability of the subsystem
    • When you need to build a hierarchical subsystem, use the Facade pattern to define the entry point of each layer of the subsystem
      • If the subsystems depend on each other, Facade can be used to communicate, simplifying the dependence between them
  3. Sample Demo

    import time
    
    SLEEP = 0.5
    
    class TC1(object):
        def run(self):
            print("###### In Test 1 ######")
            time.sleep(SLEEP)
            print("Setting up")
            time.sleep(SLEEP)
            print("Running test")
            time.sleep(SLEEP)
            print("Tearing down")
            time.sleep(SLEEP)
            print("Test 1 Finished")
    
    class TC2(object):
        def run(self):
            print("###### In Test 2 ######")
            time.sleep(SLEEP)
            print("Setting up")
            time.sleep(SLEEP)
            print("Running test")
            time.sleep(SLEEP)
            print("Tearing down")
            time.sleep(SLEEP)
            print("Test 2 Finished")
    
    class TC3(object):
        def run(self):
            print("###### In Test 3 ######")
            time.sleep(SLEEP)
            print("Setting up")
            time.sleep(SLEEP)
            print("Running test")
            time.sleep(SLEEP)
            print("Tearing down")
            time.sleep(SLEEP)
            print("Test 3 Finished")
    
    # Facade
    class TestRunner(object):
        def __init__(self):
            self.tc1 = TC1()
            self.tc2 = TC2()
            self.tc3 = TC3()
            self.tests = [i for i in (self.tc1, self.tc2, self.tc3)]
            
        def run_all(self):
            for test in self.tests:
                test.run()
    
    
    # Client
    if __name__ == '__main__':
        test_runner = TestRunner()
        test_runner.run_all()
    

11. Flyweight (Flyweight)

Flyweight

  1. effect

    With the help of sharing technology, effectively support a large number of fine-grained objects

  2. Applicable scene

    • An application uses a lot of objects, causing a lot of storage overhead
    • Most of the state of an object can be changed to an external state. If you delete the external state of an object, you can replace many groups
    • The application does not depend on object identification. Since Flyweight objects can be shared, the identification test will return true values ​​for objects that are conceptually distinct.
  3. Sample Demo

    import weakref
    
    
    class Card(object):
        _CardPool = weakref.WeakValueDictionary()
    
        def __new__(cls, value, suit):
            obj = Card._CardPool.get(value + suit, None)
            if not obj:
                obj = object.__new__(cls)
                Card._CardPool[value + suit] = obj
                obj.value, obj.suit = value, suit
            return obj
        
        def __repr__(self):
            return "<Card: %s%s>" % (self.value, self.suit) 
    
    # Client
    if __name__ == '__main__':
        c1 = Card("9", "h")
        c2 = Card("9", "h")
        print(c1, c2)
        print(c1 == c2)
        print(id(c1), id(c2))
    

12. Proxy

Proxy

  1. effect

    Provide a proxy for other objects to control access to such objects

  2. Applicable scene

    • When you need to use more general and complex object pointers instead of simple pointers, use the Proxy mode.
      • Remote proxy: Provides a local representative for an object in different address spaces
      • Virtual agent: create expensive objects as needed
      • Protection agent: Control access to the original object, for the object should have different access rights
      • Smart proxy: Instead of simple pointers, perform some additional operations when accessing objects. Typical use: counting references to actual objects.
    • When a persistent object is referenced for the first time, load it into memory
    • Before accessing an actual object, check whether a lock is already locked to ensure that other objects cannot change it
  3. Sample Demo

    import time
    
    class SaleManager(object):
        def work(self):
            print("Sale Manager is working...")
        
        def talk(self):
            print("sale Manager ready to talk.")
    
    class Proxy(object):
        def __init__(self):
            self.busy = False
            self.sales = None
    
        def work(self):
            print("Proxy checking for Sale Manager availability.")
            if not self.busy:
                self.sales = SaleManager()
                time.sleep(2)
                self.sales.talk()
            else:
                time.sleep(2)
                print("Sale Manager is busy.")
    
    # Client
    if __name__ == '__main__':
        proxy = Proxy()
        proxy.work()
        proxy.busy = True
        proxy.work()
    

13. Interpreter

Interpreter

  1. effect

    Given a language, define an expression of its grammar (Expression), and define an interpreter, the interpreter uses this expression (Expression) to interpret sentences in the interpreted language.

  2. Applicable scene

    • When a language needs interpretation and execution, and you can express the sentence in this language as an AST, you can use the interpreter mode (to be refined in detail)
  3. Sample Demo

    class Context(object):
        def __init__(self):
            self.input = ""
            self.output = ""
    
    class AbstractExpression(object):
        def interpret(self, context):
            pass
    
    class Expresssion(AbstractExpression):
        def interpret(self, context):
            print("terminal interpret")
    
    class NonterminalExpression(AbstractExpression):
        def interpret(self, context):
            print("Nonterminal interpret")
    
    
    # Client
    if __name__ == '__main__':
        context = ""
        c = []
        c.append(Expresssion())
        c.append(NonterminalExpression())
        c.append(Expresssion())
        c.append(Expresssion())
        for item in c:
            c.interpret(context)
    

14. Template Method

Template Method

  1. effect

    Define the skeleton of the algorithm in an operation, and delay some steps into subclasses. TemplateMethod allows subclasses to redefine certain steps of an algorithm without changing the structure of an algorithm.

  2. Applicable scene

    • Implement the unchanging part of an algorithm at once, and leave the variable behavior to subclasses to implement.
    • The common behavior in each subclass should be extracted and concentrated into a common parent class to avoid code duplication. This is a good example of "refactoring to generalize" as described by Opdyke and Johnson
    • Control subclass extension
  3. Sample Demo

# Skeletons
string = "apple bear cat"
def iter_elements(getter, action):
    for elem in getter():
        action(elem)

def rev_elements(getter, action):
    for elem in getter()[::-1]:
        action(elem)


# Getters
def get_list():
    return string.split()

def get_lists():
    return [list(x) for x in string.split()]

# Actions
def print_item(item):
    print(item)

def print_rev_item(item):
    print(item[::-1])

def make_templates(skeleton, getter, action):
    def template():
        skeleton(getter, action)
    
    return template


# Client
if __name__ == '__main__':
    templates = [make_templates(s, g, a) 
                    for g in (get_list, get_lists) 
                    for a in (print_item, print_rev_item) 
                    for s in (iter_elements, rev_elements)
                ]    
    for template in templates:
        template()

Guess you like

Origin www.cnblogs.com/CocoML/p/12726669.html