Veintitrés patrones de diseño

Tres patrones de diseño básicos comunes

  1. Modo de creación

    Proporcione métodos de creación de instancias y métodos de creación correspondientes para situaciones adecuadas.

  2. Modelo estructurado

    Se usa para tratar la relación entre entidades, de modo que estas entidades puedan trabajar juntas mejor.

  3. Modo de comportamiento

    Se utiliza para la comunicación entre diferentes entidades y proporciona un método de comunicación más fácil y más flexible para la comunicación entre entidades.

Introducción detallada del modo

Tipo de creación

1. Método de fábrica

Método de la fábrica

  1. Efecto

    Defina una interfaz para crear objetos y permita que las subclases decidan qué clase se debe instanciar. El Método Factory retrasa la creación de instancias de una clase en sus subclases.

  2. Escena aplicable

    • Cuando una clase no conoce la clase del objeto que debe crear.
    • Cuando una clase quiere que sus subclases especifiquen los objetos que crea.
  3. Demo de muestra

    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. Fábrica abstracta

  1. Efecto

    Proporcione una interfaz para crear objetos relacionados o interdependientes sin especificar sus clases específicas.

  2. Escena aplicable

    • Cuando un sistema es independiente de la creación, combinación y presentación de sus productos.
    • Cuando un sistema requiere una de las múltiples familias de productos para configurar
    • Al enfatizar el diseño de una serie de objetos de productos relacionados para uso conjunto
    • Al proporcionar una biblioteca de clase de producto, pero solo desea mostrar sus interfaces en lugar de la implementación.
  3. Demo de muestra

    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. Constructor

Método de la fábrica

  1. Efecto

    Separe la construcción de un objeto complejo de su representación para que el mismo proceso de construcción pueda crear diferentes representaciones.

  2. Escena aplicable

    • Cuando la creación de un objeto complejo es independiente de los subcomponentes que componen el objeto, y cómo se ensamblan los subcomponentes
    • Permitir que diferentes objetos sean creados por diferentes procesos de construcción
  3. Demo de muestra

    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. Prototipo (prototipo)

Método de la fábrica

  1. Efecto

    Use instancias de prototipos para crear objetos y copie estos prototipos para crear nuevos objetos.

  2. Escena aplicable

    • Carga dinámica, es decir, cuando la clase a instanciar se especifica en tiempo de ejecución
    • Para evitar crear un nivel de clase de fábrica paralelo al nivel de clase de producto
    • Cuando una instancia de una clase solo puede tener una combinación de varios estados diferentes

    Se recomienda que un número correspondiente de prototipos, y clonarlos, sea más conveniente que crear instancias manualmente con el estado apropiado cada vez.

  3. Demo de muestra

    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 (caso único)

Método de la fábrica

  1. Efecto

    Asegúrese de que solo exista una instancia de una clase

  2. Escena aplicable

    • Cuando la clase permite solo una instancia, y el cliente puede acceder a ella desde una variable global
    • Cuando esta instancia única se puede extender por subclases, y el cliente puede usar una instancia extendida sin cambiar el código
  3. Demo de muestra

    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)
    

Tipo estructural

6. Adaptador de clase / objeto (adaptador)

Método de la fábrica

  1. Efecto

    Convierta la interfaz de una clase a otra interfaz que el cliente desee. El modo Adaptador permite que las clases que no pueden funcionar juntas debido a interfaces incompatibles funcionen juntas nuevamente.

  2. Escena aplicable

    • Intentar usar una clase existente cuya interfaz no satisfaga sus necesidades
    • Intentar crear una clase reutilizable que pueda funcionar con otras clases no relacionadas o impredecibles
    • Intentando usar subclases existentes, pero no puede subclasificar cada una para que coincida con su interfaz.
  3. Demo de muestra

    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. puente

Método de la fábrica

  1. Efecto

    Separe la parte abstracta y su parte de realización, para que puedan cambiar independientemente

  2. Escena aplicable

    • Intenta desacoplar la abstracción y la parte de realización.
    • La abstracción de la clase y su implementación se puede ampliar generando subclases.
    • La modificación de una parte de implementación abstracta no debería afectar al cliente, es decir, el cliente no necesita ser recompilado.
  3. Demo de muestra

    # 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. Compuesto

Compuesto

  1. Efecto

    La combinación de objetos en una estructura de árbol para representar una jerarquía de parte completa hace que el uso de objetos individuales y objetos combinados sea más consistente.

  2. Escena aplicable

    • Necesidad de jerarquía parcial
    • Espero que el cliente ignore la diferencia entre el objeto combinado y el objeto único y use la estructura combinada de manera uniforme.
  3. Demo de muestra

    # 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. Decorador (decorador)

Decorador

  1. Efecto

    Agregue algunas características adicionales a un objeto dinámicamente. En términos de uso, el patrón decorador es más flexible que la forma de generar subclases.

  2. Escena aplicable

    • Agregue responsabilidades a objetos individuales de forma dinámica y transparente sin afectar a otros objetos
    • Manejo de deberes revocables
    • Cuando el método de generación de subclases no se puede utilizar para la expansión:
      • Puede haber una gran cantidad de extensiones independientes. Para admitir cada combinación, se generará una gran cantidad de subclases y la cantidad de subclases explotará.
      • Las definiciones de clase pueden estar ocultas, o las definiciones de clase no pueden usarse para generar subclases
  3. Demo de muestra

    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. Fachada (apariencia)

Fachada

  1. Efecto

    Para proporcionar una interfaz consistente para un conjunto de interfaces del subsistema, el modo Fachada define una interfaz de alto nivel, haciendo que el subsistema sea más fácil de usar.

  2. Escena aplicable

    • Cuando el subsistema se complica debido a la evolución continua, tratando de proporcionar una interfaz simple para el subsistema complejo
    • Cuando se utilizan la mayoría de los patrones, se generarán más clases y más pequeñas, intentando reutilizar subclases o personalizar el subsistema
    • Existe una gran dependencia entre el cliente y la implementación de la clase abstracta. La introducción de Facade puede separar el subsistema del cliente y otros subsistemas, mejorando la independencia y portabilidad del subsistema
    • Cuando necesite construir un subsistema jerárquico, use el patrón Fachada para definir el punto de entrada de cada capa del subsistema
      • Si los subsistemas dependen unos de otros, Facade se puede utilizar para comunicarse, lo que simplifica la dependencia entre ellos.
  3. Demo de muestra

    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)

Peso mosca

  1. Efecto

    Con la ayuda de compartir tecnología, soporta efectivamente una gran cantidad de objetos de grano fino

  2. Escena aplicable

    • Una aplicación utiliza muchos objetos, lo que genera una gran sobrecarga de almacenamiento
    • La mayor parte del estado de un objeto se puede cambiar a un estado externo. Si elimina el estado externo de un objeto, puede reemplazar muchos grupos con relativamente pocos objetos compartidos.
    • La aplicación no depende de la identificación del objeto. Dado que los objetos Flyweight se pueden compartir, la prueba de identificación devolverá valores verdaderos para los objetos que son conceptualmente distintos.
  3. Demo de muestra

    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

Apoderado

  1. Efecto

    Proporcionar un proxy para otros objetos para controlar el acceso a dichos objetos.

  2. Escena aplicable

    • Cuando necesite usar punteros de objetos más generales y complejos en lugar de punteros simples, use el modo Proxy.
      • Proxy remoto: proporciona un representante local para un objeto en diferentes espacios de direcciones
      • Agente virtual: cree objetos caros según sea necesario
      • Agente de protección: controle el acceso al objeto original, ya que el objeto debe tener diferentes derechos de acceso
      • Proxy inteligente: en lugar de simples punteros, realice algunas operaciones adicionales al acceder a los objetos. Uso típico: contar referencias a objetos reales.
    • Cuando se hace referencia a un objeto persistente por primera vez, cárguelo en la memoria
    • Antes de acceder a un objeto real, verifique si un bloqueo ya está bloqueado para asegurarse de que otros objetos no puedan cambiarlo.
  3. Demo de muestra

    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. intérprete

Interprete

  1. Efecto

    Dado un lenguaje, define una expresión de su gramática (Expresión) y define un intérprete, el intérprete usa esta expresión (Expresión) para interpretar oraciones en el idioma interpretado.

  2. Escena aplicable

    • Cuando un idioma necesita interpretación y ejecución, y puede expresar la oración en este idioma como AST, puede usar el modo de intérprete (para refinarlo en detalle)
  3. Demo de muestra

    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. Método de plantilla

Método de plantilla

  1. Efecto

    Definir el esqueleto del algoritmo en una operación y retrasar algunos pasos en subclases. TemplateMethod permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar la estructura de un algoritmo.

  2. Escena aplicable

    • Implemente la parte inalterable de un algoritmo de una vez, y deje el comportamiento variable a las subclases para implementar.
    • Los comportamientos comunes en cada subclase deben extraerse y concentrarse en una clase principal común para evitar la duplicación de código. Este es un buen ejemplo de "refactorización para generalizar" según lo descrito por Opdyke y Johnson
    • Extensión de subclase de control
  3. Demo de muestra

# 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()

Supongo que te gusta

Origin www.cnblogs.com/CocoML/p/12726669.html
Recomendado
Clasificación