程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Design Patterns - Python version

編輯:Python
Home

List of articles

    • Creation type
      • 1. Factory Method( Factory method )
      • 2. Abstract Factory( Abstract factory )
      • 3. Builder( builder )
      • 4. Prototype( Prototype )
      • 5. Singleton( Single case )
    • Structural type
      • 6. Adapter Class/Object( Adapter )
      • 7. Bridge( The bridge )
      • 8. Composite( Combine )
      • 9. Decorator( decorate )
      • 10. Facade( appearance )
      • 11. Flyweight( Enjoying yuan )
      • 12. Proxy( agent )
    • Behavior type
      • 13. Interpreter( Interpreter )
      • 14. Template Method( Template method )
      • 15. Chain of Responsibility( Responsibility chain )
      • 16. Command( command )
      • 17. Iterator( iterator )
      • 18. Mediator( Intermediary )
      • 19. Memento( Memorandum )
      • 20. Observer( The observer )
      • 21. State( state )
      • 22. Strategy( Strategy )
      • 23. Visitor( The visitor )

The source code of this article is sent to github:https://github.com/w392807287/Design_pattern_of_python

reference :

《 Big talk design patterns 》 —— Wu Qiang
《Python Design patterns 》—— pythontip.com
《23 Design patterns 》—— http://www.cnblogs.com/beijiguangyong/

What is the design pattern ?

Design pattern is a summary 、 Optimization of the , Reusable solutions to some of the programming problems we often encounter . A design pattern does not directly affect our code like a class or a library . conversely , The design pattern is more advanced , It's a method template that must be implemented in a specific situation . Design patterns do not bind specific programming languages . A good design pattern should be able to be implemented in most programming languages ( If you can't do it all , It depends on the characteristics of the language ). The most important thing is , Design pattern is also a double-edged sword , If the design pattern is used improperly, it will cause disaster , And then bring endless troubles . However, if the design pattern is used in the right place at the right time , It will be your Savior .

At first , You will think “ Pattern ” It's a wise move to solve a specific problem . That's right. , It looks like it's really through a lot of people working together , The most common way to look at problems from different perspectives 、 The most flexible solution . Maybe you have seen or solved these problems before , But your solution may not be as complete as the pattern .

Although known as “ Design patterns ”, But they are the same “ Design “ The field is not closely linked . The simultaneous interpreting of design patterns and traditional meanings 、 Design and implementation are different , In fact, the design pattern roots a complete idea in the program , So it could be in the analysis stage or higher design stage . It's interesting because the design pattern is embodied in the program code , So it might make you think it won't come before the concrete implementation phase ( In fact, you didn't realize that you were using a specific design pattern before entering the specific implementation phase ).

Patterns can be understood through the basic concepts of programming : Add an abstraction layer . To abstract a thing is to isolate any concrete details , The purpose of this is to separate the core parts from other details . When you find that some parts of your program often change for some reason , And you don't want the parts of these changes to cause the other parts to change , At this time, you need to think about design methods that will not change . Doing so not only makes the code more maintainable , And it makes the code easier to understand , To reduce development costs .

Here are three basic design patterns :

  • Create mode , Provide instantiation methods , Provides the corresponding object creation method for the appropriate situation .
  • Structured patterns , Usually used to deal with the relationship between entities , So that these entities can work better together .
  • Patterns of behavior , For communication between different entities , Easier communication between entities , More flexible means of communication .

Creation type

1. Factory Method( Factory method )

Back

Intention :

Defines an interface for creating objects , Let the subclass decide which class to instantiate .Factory Method Delay the instantiation of a class to its subclasses .

Applicability :

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 .

When a class delegates the responsibility of creating an object to one of several helper subclasses , And when you want to localize the information about which helper subclass is the agent .

Realization :

#!/usr/bin/python
#coding:utf8
''' Factory Method '''
class ChinaGetter:
"""A simple localizer a la gettext"""
def __init__(self):
self.trans = dict(dog=u" puppy ", cat=u" kitten ")
def get(self, msgid):
"""We'll punt if we don't have a translation"""
try:
return self.trans[msgid]
except KeyError:
return str(msgid)
class EnglishGetter:
"""Simply echoes the msg ids"""
def get(self, msgid):
return str(msgid)
def get_localizer(language="English"):
"""The factory method"""
languages = dict(English=EnglishGetter, China=ChinaGetter)
return languages[language]()
# Create our localizers
e, g = get_localizer("English"), get_localizer("China")
# Localize some text
for msgid in "dog parrot cat bear".split():
print(e.get(msgid), g.get(msgid))

2. Abstract Factory( Abstract factory )

Back

Intention :

Provides an interface for creating a series of related or interdependent objects , Without specifying their specific classes .

Applicability :

A system should be independent of its product creation 、 When combining and expressing .

When a system is to be configured by one of multiple product families .

When you want to emphasize the design of a series of related product objects for joint use .

When you provide a product class library , And just want to show their interfaces instead of the implementation time .

Realization :

#!/usr/bin/python
#coding:utf8
''' Abstract Factory '''
import random
class PetShop:
"""A pet shop"""
def __init__(self, animal_factory=None):
"""pet_factory is our abstract factory. We can set it at will."""
self.pet_factory = animal_factory
def show_pet(self):
"""Creates and shows a pet using the abstract factory"""
pet = self.pet_factory.get_pet()
print("This is a lovely", str(pet))
print("It says", pet.speak())
print("It eats", self.pet_factory.get_food())
# Stuff that our factory makes
class Dog:
def speak(self):
return "woof"
def __str__(self):
return "Dog"
class Cat:
def speak(self):
return "meow"
def __str__(self):
return "Cat"
# Factory classes
class DogFactory:
def get_pet(self):
return Dog()
def get_food(self):
return "dog food"
class CatFactory:
def get_pet(self):
return Cat()
def get_food(self):
return "cat food"
# Create the proper family
def get_factory():
"""Let's be dynamic!"""
return random.choice([DogFactory, CatFactory])()
# Show pets with various factories
if __name__ == "__main__":
shop = PetShop()
for i in range(3):
shop.pet_factory = get_factory()
shop.show_pet()
print("=" * 20)

3. Builder( builder )

Back

Intention :

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

Applicability :

When the algorithm for creating complex objects should be independent of the components of the object and how they are assembled .

When the construction process must allow different representations of the object being constructed .

Realization :

#!/usr/bin/python
#coding:utf8
""" Builder """
# Director
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.building
# Abstract Builder
class Builder(object):
def __init__(self):
self.building = None
def new_building(self):
self.building = Building()
# Concrete Builder
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 = 'More than One'
def build_size(self):
self.building.size = 'Small'
# Product
class Building(object):
def __init__(self):
self.floor = None
self.size = None
def __repr__(self):
return 'Floor: %s | Size: %s' % (self.floor, self.size)
# Client
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 )

Back

Intention :

Using prototype instances to specify the kind of objects to create , And create new objects by copying these stereotypes .

Applicability :

When the class to be instantiated is specified at runtime , for example , By dynamic loading ; Or to avoid creating a factory class hierarchy parallel to the product class hierarchy ; Or when an instance of a class can only have one of several different combinations of States . It may be more convenient to build the corresponding number of prototypes and clone them than to instantiate the class manually with the appropriate state each time .

Realization :

#!/usr/bin/python
#coding:utf8
''' Prototype '''
import copy
class Prototype:
def __init__(self):
self._objects = {
}
def register_object(self, name, obj):
"""Register an object"""
self._objects[name] = obj
def unregister_object(self, name):
"""Unregister an object"""
del self._objects[name]
def clone(self, name, **attr):
"""Clone a registered object and update inner attributes dictionary"""
obj = copy.deepcopy(self._objects.get(name))
obj.__dict__.update(attr)
return obj
def main():
class A:
def __str__(self):
return "I am 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 )

Back

Intention :

Make sure there is only one instance of a class , And provide a global access point to access it .

Applicability :

When a class can have only one instance and a client can access it from a well-known access point .

When the only instance should be extensible by subclassing , And customers should be able to use an extended instance without changing the code .

Realization :

#!/usr/bin/python
#coding:utf8
''' Singleton '''
class Singleton(object):
''''' A python style singleton '''
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
org = super(Singleton, cls)
cls._instance = org.__new__(cls, *args, **kw)
return cls._instance
if __name__ == '__main__':
class SingleSpam(Singleton):
def __init__(self, s):
self.s = s
def __str__(self):
return self.s
s1 = SingleSpam('spam')
print id(s1), s1
s2 = SingleSpam('spa')
print id(s2), s2
print id(s1), s1

Structural type

6. Adapter Class/Object( Adapter )

Back

Intention :

Convert the interface of one class to another that the customer wants .Adapter The pattern allows classes that would otherwise not work together because of incompatible interfaces to work together .

Applicability :

You want to use an existing class , And its interface doesn't meet your needs .

You want to create a reusable class , This class can be associated with other unrelated classes or unforeseen classes ( Classes whose interfaces may or may not be compatible ) Working together .

( Applies only to objects Adapter ) You want to use some subclass that already exists , But it is impossible to subclass each one to match their interface . An object adapter can adapt to its superclass interface .

Realization :

#!/usr/bin/python
#coding:utf8
''' Adapter '''
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%s" % ("!" * octane_level)
class Adapter(object):
""" Adapts an object by replacing methods. Usage: dog = Dog dog = Adapter(dog, dict(make_noise=dog.bark)) """
def __init__(self, obj, adapted_methods):
"""We set the adapted methods in the object's dict"""
self.obj = obj
self.__dict__.update(adapted_methods)
def __getattr__(self, attr):
"""All non-adapted calls are passed to the object"""
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 "A", obj.name, "goes", obj.make_noise()
if __name__ == "__main__":
main()

7. Bridge( The bridge )

Back

Intention :

Separate the abstract from its implementation , So that they can all change independently .

Applicability :

You don't want to have a fixed binding between the abstract and its implementation part . For example, this may be because , The implementation part can be selected or switched when the program is running .

The abstraction of a class and its implementation should be extended by generating subclasses . At this time Bridge Patterns allow you to combine different abstract interfaces and implementation parts , And expand them respectively .

Changes to an abstract implementation should have no impact on the customer , That is, the customer's code does not have to be recompiled .

(C++) You want to completely hide the abstract implementation from the client . stay C++ in , The representation of a class is visible in the class interface .

There are many classes to generate . Such a kind of hierarchy shows that you must decompose an object into two parts .Rumbaugh Call this kind of hierarchy “ Nested generalization ”(nested generalizations ).

You want to share the implementation between multiple objects ( Reference counting may be used ), But at the same time ask the customer not to know this . A simple example is Coplien Of String class [ Cop92 ], In this class, multiple objects can share the same string representation (StringRep).

Realization :

#!/usr/bin/python
#coding:utf8
''' Bridge '''
# ConcreteImplementor 1/2
class DrawingAPI1(object):
def draw_circle(self, x, y, radius):
print('API1.circle at {}:{} radius {}'.format(x, y, radius))
# ConcreteImplementor 2/2
class DrawingAPI2(object):
def draw_circle(self, x, y, radius):
print('API2.circle at {}:{} radius {}'.format(x, y, radius))
# Refined 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 specific
def draw(self):
self._drawing_api.draw_circle(self._x, self._y, self._radius)
# high-level i.e. Abstraction specific
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( Combine )

Back

Intention :

Combine objects into a tree structure to represent “ part - whole ” Hierarchical structure .C o m p o s i t e Make the use of single object and composite object consistent .

Applicability :

You want to represent the part of the object - Global hierarchy .

You want users to ignore the difference between a composite object and a single object , All objects in the composite structure are used uniformly by the user .

Realization :

#!/usr/bin/python
#coding:utf8
""" Composite """
class Component:
def __init__(self,strName):
self.m_strName = strName
def Add(self,com):
pass
def Display(self,nDepth):
pass
class Leaf(Component):
def Add(self,com):
print "leaf can't add"
def Display(self,nDepth):
strtemp = "-" * nDepth
strtemp=strtemp+self.m_strName
print strtemp
class Composite(Component):
def __init__(self,strName):
self.m_strName = strName
self.c = []
def Add(self,com):
self.c.append(com)
def Display(self,nDepth):
strtemp = "-"*nDepth
strtemp=strtemp+self.m_strName
print strtemp
for com in self.c:
com.Display(nDepth+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( decorate )

Back

Intention :

Dynamically add some additional responsibilities to an object . In terms of adding functionality ,Decorator Patterns are more flexible than subclasses .
Applicability :

Without affecting other objects , Dynamic 、 Add responsibilities to individual objects transparently .

Deal with responsibilities that can be revoked .

When you can't extend by generating subclasses . One situation is , There may be a large number of independent extensions , To support each combination will produce a large number of subclasses , It makes the number of subclasses increase explosively . Another possibility is that the class definition is hidden , Or class definitions cannot be used to generate subclasses .

Realization :

#!/usr/bin/python
#coding:utf8
''' Decorator '''
class foo(object):
def f1(self):
print("original f1")
def f2(self):
print("original f2")
class foo_decorator(object):
def __init__(self, decoratee):
self._decoratee = decoratee
def f1(self):
print("decorated f1")
self._decoratee.f1()
def __getattr__(self, name):
return getattr(self._decoratee, name)
u = foo()
v = foo_decorator(u)
v.f1()
v.f2()

10. Facade( appearance )

Back

Intention :

Provides a consistent interface for a set of interfaces in a subsystem ,Facade A pattern defines a high-level interface , This interface makes this subsystem easier to use .

Applicability :

When you want to provide a simple interface to a complex subsystem . Subsystems tend to become more and more complex as they evolve . Most patterns produce more and smaller classes when used . This makes the subsystem more reusable , It's also easier to customize subsystems , But it also brings some difficulties to users who don't need to customize the subsystem .Facade You can provide a simple default view , This view is sufficient for most users , And those who need more customization can go over facade layer .

There is a huge dependency between the client and the implementation of the abstract class . introduce facade Separate this subsystem from customers and other subsystems , It can improve the independence and portability of the subsystem .

When you need to build a hierarchical subsystem , Use facade The pattern defines the entry point of each layer in the subsystem . If subsystems are interdependent , You can just let them go through facade To communicate , Thus, the dependence between them is simplified .

Realization :

#!/usr/bin/python
#coding:utf8
''' Decorator '''
import time
SLEEP = 0.5
# Complex Parts
class TC1:
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 Finished\n")
class TC2:
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 Finished\n")
class TC3:
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 Finished\n")
# Facade
class TestRunner:
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 runAll(self):
[i.run() for i in self.tests]
# Client
if __name__ == '__main__':
testrunner = TestRunner()
testrunner.runAll()

11. Flyweight( Enjoying yuan )

Back

Intention :

Using sharing technology to effectively support a large number of fine-grained objects .

Applicability :

An application uses a lot of objects .

It's all about using a lot of objects , Cause 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 , Then you can replace many group objects with relatively few shared objects .

The application does not depend on the object identity . because Flyweight Objects can be shared , There's obviously something else in the concept , The identity test will return a true value .

Realization :

#!/usr/bin/python
#coding:utf8
''' Flyweight '''
import weakref
class Card(object):
"""The object pool. Has builtin reference counting"""
_CardPool = weakref.WeakValueDictionary()
"""Flyweight implementation. If the object exists in the pool just return it (instead of creating a new one)"""
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 __init__(self, value, suit): 
# self.value, self.suit = value, suit 
def __repr__(self):
return "<Card: %s%s>" % (self.value, self.suit)
if __name__ == '__main__':
# comment __new__ and uncomment __init__ to see the difference
c1 = Card('9', 'h')
c2 = Card('9', 'h')
print(c1, c2)
print(c1 == c2)
print(id(c1), id(c2))

12. Proxy( agent )

Back

Intention :

Provides a proxy for other objects to control access to this object .

Applicability :

When you need to replace a simple pointer with a more general and complex object pointer , Use Proxy Pattern . Here's a Some can be used Proxy Patterns are common :

  1. Remote agent (Remote Proxy ) Provide local representation of an object in different address spaces . NEXTSTEP[Add94] Use NXProxy Class does this .Coplien[Cop92] Call this agent “ Ambassador ” (Ambassador ).
    2 ) Virtual agent (Virtual Proxy ) Create expensive objects as needed . Described in the motivation section ImageProxy This is an example of such an agent .
  2. Protection agency (Protection Proxy ) Control access to the original object . Protection agents should be used differently for objects When you have access to . for example , stay Choices operating system [ CIRM93] in KemelProxies Provide... For operating system objects Access protection .
    4 ) Intelligent guidance (Smart Reference ) Instead of a simple pointer , It performs some additional operations when accessing objects . Its typical uses include : Count references to actual objects , So when the object has no references , It can be released automatically ( Also known as SmartPointers[Ede92 ] ).

When a persistent object is referenced for the first time , Load it into memory .

Before accessing an actual object , Check if it's locked , To ensure that other objects cannot change it .

Realization :

#!/usr/bin/python
#coding:utf8
''' Proxy '''
import time
class SalesManager:
def work(self):
print("Sales Manager working...")
def talk(self):
print("Sales Manager ready to talk")
class Proxy:
def __init__(self):
self.busy = 'No'
self.sales = None
def work(self):
print("Proxy checking for Sales Manager availability")
if self.busy == 'No':
self.sales = SalesManager()
time.sleep(2)
self.sales.talk()
else:
time.sleep(2)
print("Sales Manager is busy")
if __name__ == '__main__':
p = Proxy()
p.work()
p.busy = 'Yes'
p.work()

Behavior type

13. Interpreter( Interpreter )

Back

Intention :

Given a language , A representation of the grammar that defines it , And define an interpreter , This interpreter uses this representation to interpret sentences in a language .

Applicability :

When there is a language that needs to be interpreted and executed , And you can express the sentences in the language as an abstract grammar tree , You can use interpreter mode . This mode works best when there are the following situations :

The grammar is simple. For complex grammar , The class level of grammar becomes too large to manage . At this time, a tool like parser generator is a better choice . They can interpret expressions without building an abstract syntax tree , This can save space and possibly time .

Efficiency is not a key issue. The most efficient interpreter is usually not implemented by directly interpreting the parsing tree , But first convert them into another form . for example , Regular expressions are usually converted into state machines . But even in this case , The converter can still be implemented in interpreter mode , This pattern is still useful .

Realization :

#!/usr/bin/python
#coding:utf8
''' Interpreter '''
class Context:
def __init__(self):
self.input=""
self.output=""
class AbstractExpression:
def Interpret(self,context):
pass
class Expression(AbstractExpression):
def Interpret(self,context):
print "terminal interpret"
class NonterminalExpression(AbstractExpression):
def Interpret(self,context):
print "Nonterminal interpret"
if __name__ == "__main__":
context= ""
c = []
c = c + [Expression()]
c = c + [NonterminalExpression()]
c = c + [Expression()]
c = c + [Expression()]
for a in c:
a.Interpret(context)

14. Template Method( Template method )

Back

Intention :

Defines the skeleton of an algorithm in an operation , Instead, defer some steps to subclasses .TemplateMethod Allows subclasses to redefine certain steps of an algorithm without changing its structure .

Applicability :

Implement the invariant part of an algorithm at once , And leave the variable behavior to the subclass to implement .

The common behavior in each subclass should be extracted and concentrated in a common parent class to avoid code duplication . This is a Opdyke and Johnson Described “ Decomposing to generalize ” A good example of [ OJ93 ]. First identify the differences in the existing code , And separate the differences into new operations . Last , Replace the different code with a template method that calls these new operations .

Control subclass extension . Template methods are only called at specific points “hook ” operation ( See the effects section ), This allows only extension at these points .

Realization :

#!/usr/bin/python
#coding:utf8
''' Template Method '''
ingredients = "spam eggs apple"
line = '-' * 10
# Skeletons
def iter_elements(getter, action):
"""Template skeleton that iterates items"""
for element in getter():
action(element)
print(line)
def rev_elements(getter, action):
"""Template skeleton that iterates items in reverse order"""
for element in getter()[::-1]:
action(element)
print(line)
# Getters
def get_list():
return ingredients.split()
def get_lists():
return [list(x) for x in ingredients.split()]
# Actions
def print_item(item):
print(item)
def reverse_item(item):
print(item[::-1])
# Makes templates
def make_template(skeleton, getter, action):
"""Instantiate a template method with getter and action"""
def template():
skeleton(getter, action)
return template
# Create our template functions
templates = [make_template(s, g, a)
for g in (get_list, get_lists)
for a in (print_item, reverse_item)
for s in (iter_elements, rev_elements)]
# Execute them
for template in templates:
template()

15. Chain of Responsibility( Responsibility chain )

Back

Intention :

Give multiple objects a chance to process requests , Thus, the coupling relationship between the sender and the receiver of the request is avoided . Link these objects into a chain , And pass the request along the chain , Until an object handles it .

Applicability :

There are multiple objects that can handle a request , Which object handles the request automatically at run time .

You want to... Without specifying the recipient , Submit a request to one of multiple objects .

The set of objects that can handle a request should be specified dynamically .

Realization :

#!/usr/bin/python
#coding:utf8
""" Chain """
class Handler:
def successor(self, successor):
self.successor = successor
class ConcreteHandler1(Handler):
def handle(self, request):
if request > 0 and request <= 10:
print("in handler1")
else:
self.successor.handle(request)
class ConcreteHandler2(Handler):
def handle(self, request):
if request > 10 and request <= 20:
print("in handler2")
else:
self.successor.handle(request)
class ConcreteHandler3(Handler):
def handle(self, request):
if request > 20 and request <= 30:
print("in handler3")
else:
print('end of chain, no handler for {}'.format(request))
class Client:
def __init__(self):
h1 = ConcreteHandler1()
h2 = ConcreteHandler2()
h3 = ConcreteHandler3()
h1.successor(h2)
h2.successor(h3)
requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
for request in requests:
h1.handle(request)
if __name__ == "__main__":
client = Client()

16. Command( command )

Back

Intention :

Encapsulate a request as an object , This allows you to parameterize customers with different requests ; Queues or logs requests , And support undo operations .

Applicability :

Abstract the action to be executed to parameterize an object , You can use callbacks in procedural language (call back) Functions express this parameterization mechanism . The so-called callback function means that the function is registered somewhere first , And it will be called later when needed .Command Pattern is an object-oriented alternative to callback mechanism .

Designate... At different times 、 Arrange and execute requests . One Command An object can have a lifetime independent of the initial request . If the receiver of a request can be expressed in an address space independent way , Then the command object responsible for the request can be passed to another different process and the request can be implemented there .

Support cancel operation .Command Of Excute The operation can store the state before the operation is implemented , When canceling an operation, this state is used to eliminate the impact of the operation .Command Interface must add a Unexecute operation , This action cancels the last Execute The effect of the call . The executed command is stored in a history list . You can traverse this list backward and forward and call... Respectively Unexecute and Execute To achieve unlimited multiplicity “ Cancel ” and “ redo ”.

Support log modification , So when the system crashes , These changes can be redone . stay Command Add mount operation and storage operation to the interface , Can be used to keep a consistent log of changes . The process of recovering from a crash involves rereading recorded commands from disk and using Execute Operations re execute them .

Build a system with high-level operations built on primitive operations . Such a structure supports transactions ( transaction) Is very common in information systems . A transaction encapsulates a set of changes to data .Command Patterns provide a way to model transactions .Command There is a public interface , So that you can call all transactions in the same way . It is also easy to add new transactions to extend the system .

Realization :

#!/usr/bin/python
#coding:utf8
""" Command """
import os
class MoveFileCommand(object):
def __init__(self, src, dest):
self.src = src
self.dest = dest
def execute(self):
self()
def __call__(self):
print('renaming {} to {}'.format(self.src, self.dest))
os.rename(self.src, self.dest)
def undo(self):
print('renaming {} to {}'.format(self.dest, self.src))
os.rename(self.dest, self.src)
if __name__ == "__main__":
command_stack = []
# commands are just pushed into the command stack
command_stack.append(MoveFileCommand('foo.txt', 'bar.txt'))
command_stack.append(MoveFileCommand('bar.txt', 'baz.txt'))
# they can be executed later on
for cmd in command_stack:
cmd.execute()
# and can also be undone at will
for cmd in reversed(command_stack):
cmd.undo()

17. Iterator( iterator )

Back

Intention :

Provides a way to access elements of an aggregate object in sequence , Without exposing the internal representation of the object .

Applicability :

Access the contents of an aggregate object without exposing its internal representation .

Support multiple traversal of aggregate objects .

Provide a unified interface for traversing different aggregation structures ( namely , Support polymorphic iteration ).

Realization :

#!/usr/bin/python
#coding:utf8
''' Interator '''
def count_to(count):
"""Counts by word numbers, up to a maximum of five"""
numbers = ["one", "two", "three", "four", "five"]
# enumerate() returns a tuple containing a count (from start which
# defaults to 0) and the values obtained from iterating over sequence
for pos, number in zip(range(count), numbers):
yield number
# Test the generator
count_to_two = lambda: count_to(2)
count_to_five = lambda: count_to(5)
print('Counting to two...')
for number in count_to_two():
print number
print " "
print('Counting to five...')
for number in count_to_five():
print number
print " "

18. Mediator( Intermediary )

Back

Intention :

Encapsulate a series of object interactions with a mediation object . Mediators make it unnecessary for objects to explicitly refer to each other , So that the coupling is loose , And they can change their interaction independently .

Applicability :

A set of objects communicates in a well-defined but complex way . The resulting interdependence is disorganized and difficult to understand .

An object references many other objects and communicates directly with them , Making it difficult to reuse the object .

Want to customize a behavior that is distributed across multiple classes , And don't want to generate too many subclasses .

Realization :

#!/usr/bin/python
#coding:utf8
''' Mediator '''
"""http://dpip.testingperspective.com/?p=28"""
import time
class TC:
def __init__(self):
self._tm = tm
self._bProblem = 0
def setup(self):
print("Setting up the Test")
time.sleep(1)
self._tm.prepareReporting()
def execute(self):
if not self._bProblem:
print("Executing the test")
time.sleep(1)
else:
print("Problem in setup. Test not executed.")
def tearDown(self):
if not self._bProblem:
print("Tearing down")
time.sleep(1)
self._tm.publishReport()
else:
print("Test not executed. No tear down required.")
def setTM(self, TM):
self._tm = tm
def setProblem(self, value):
self._bProblem = value
class Reporter:
def __init__(self):
self._tm = None
def prepare(self):
print("Reporter Class is preparing to report the results")
time.sleep(1)
def report(self):
print("Reporting the results of Test")
time.sleep(1)
def setTM(self, TM):
self._tm = tm
class DB:
def __init__(self):
self._tm = None
def insert(self):
print("Inserting the execution begin status in the Database")
time.sleep(1)
#Following code is to simulate a communication from DB to TC
import random
if random.randrange(1, 4) == 3:
return -1
def update(self):
print("Updating the test results in Database")
time.sleep(1)
def setTM(self, TM):
self._tm = tm
class TestManager:
def __init__(self):
self._reporter = None
self._db = None
self._tc = None
def prepareReporting(self):
rvalue = self._db.insert()
if rvalue == -1:
self._tc.setProblem(1)
self._reporter.prepare()
def setReporter(self, reporter):
self._reporter = reporter
def setDB(self, db):
self._db = db
def publishReport(self):
self._db.update()
rvalue = self._reporter.report()
def setTC(self, tc):
self._tc = tc
if __name__ == '__main__':
reporter = Reporter()
db = DB()
tm = TestManager()
tm.setReporter(reporter)
tm.setDB(db)
reporter.setTM(tm)
db.setTM(tm)
# For simplification we are looping on the same test.
# Practically, it could be about various unique test classes and their
# objects
while (True):
tc = TC()
tc.setTM(tm)
tm.setTC(tc)
tc.setup()
tc.execute()
tc.tearDown()

19. Memento( Memorandum )

Back

Intention :

Without breaking encapsulation , Capture the internal state of an object , And save the state outside the object . In this way, the object can be restored to the original saved state later .

Applicability :

Must save an object at a certain time ( part ) state , So that it can return to its previous state when it needs to be later .

If one uses an interface to let other objects get these states directly , It will expose the implementation details of the object and destroy the encapsulation of the object .

Realization :

#!/usr/bin/python
#coding:utf8
''' Memento '''
import copy
def Memento(obj, deep=False):
state = (copy.copy, copy.deepcopy)[bool(deep)](obj.__dict__)
def Restore():
obj.__dict__.clear()
obj.__dict__.update(state)
return Restore
class Transaction:
"""A transaction guard. This is really just syntactic suggar arount a memento closure. """
deep = False
def __init__(self, *targets):
self.targets = targets
self.Commit()
def Commit(self):
self.states = [Memento(target, self.deep) for target in self.targets]
def Rollback(self):
for st in self.states:
st()
class transactional(object):
"""Adds transactional semantics to methods. Methods decorated with @transactional will rollback to entry state upon exceptions. """
def __init__(self, method):
self.method = method
def __get__(self, obj, T):
def transaction(*args, **kwargs):
state = Memento(obj)
try:
return self.method(obj, *args, **kwargs)
except:
state()
raise
return transaction
class NumObj(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return '<%s: %r>' % (self.__class__.__name__, self.value)
def Increment(self):
self.value += 1
@transactional
def DoStuff(self):
self.value = '1111' # <- invalid value
self.Increment() # <- will fail and rollback
if __name__ == '__main__':
n = NumObj(-1)
print(n)
t = Transaction(n)
try:
for i in range(3):
n.Increment()
print(n)
t.Commit()
print('-- commited')
for i in range(3):
n.Increment()
print(n)
n.value += 'x' # will fail
print(n)
except:
t.Rollback()
print('-- rolled back')
print(n)
print('-- now doing stuff ...')
try:
n.DoStuff()
except:
print('-> doing stuff failed!')
import traceback
traceback.print_exc(0)
pass
print(n)

20. Observer( The observer )

Back

Intention :

Defines a one-to-many dependency between objects , When the state of an object changes , All objects that depend on it are notified and automatically updated .

Applicability :

When an abstract model has two sides , One aspect depends on the other . Encapsulate the two in separate objects so that they can be changed and reused independently .

When the change of one object needs to change other objects at the same time , I don't know how many objects need to be changed .

When an object has to notify other objects , And it can't assume who other objects are . In other words , You don't want these objects to be tightly coupled .

Realization :

#!/usr/bin/python
#coding:utf8
''' Observer '''
class Subject(object):
def __init__(self):
self._observers = []
def attach(self, observer):
if not observer in self._observers:
self._observers.append(observer)
def detach(self, observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self, modifier=None):
for observer in self._observers:
if modifier != observer:
observer.update(self)
# Example usage
class Data(Subject):
def __init__(self, name=''):
Subject.__init__(self)
self.name = name
self._data = 0
@property
def data(self):
return self._data
@data.setter
def data(self, value):
self._data = value
self.notify()
class HexViewer:
def update(self, subject):
print('HexViewer: Subject %s has data 0x%x' %
(subject.name, subject.data))
class DecimalViewer:
def update(self, subject):
print('DecimalViewer: Subject %s has data %d' %
(subject.name, subject.data))
# Example usage...
def main():
data1 = Data('Data 1')
data2 = Data('Data 2')
view1 = DecimalViewer()
view2 = HexViewer()
data1.attach(view1)
data1.attach(view2)
data2.attach(view2)
data2.attach(view1)
print("Setting Data 1 = 10")
data1.data = 10
print("Setting Data 2 = 15")
data2.data = 15
print("Setting Data 1 = 3")
data1.data = 3
print("Setting Data 2 = 5")
data2.data = 5
print("Detach HexViewer from data1 and data2.")
data1.detach(view2)
data2.detach(view2)
print("Setting Data 1 = 10")
data1.data = 10
print("Setting Data 2 = 15")
data2.data = 15
if __name__ == '__main__':
main()

21. State( state )

Back

Intention :

Allows an object to change its behavior when its internal state changes . Object appears to modify its class .

Applicability :

The behavior of an object depends on its state , And it has to change its behavior according to the state at runtime .

An operation contains a large number of branch conditional statements , And these branches depend on the state of the object . This state is usually represented by one or more enumeration constants . Usually , Multiple operations contain the same conditional structure .State The pattern places each conditional branch into a separate class . This allows you to treat the state of an object as an object according to its own situation , This object can change independently of other objects .

Realization :

#!/usr/bin/python
#coding:utf8
''' State '''
class State(object):
"""Base state. This is to share functionality"""
def scan(self):
"""Scan the dial to the next station"""
self.pos += 1
if self.pos == len(self.stations):
self.pos = 0
print("Scanning... Station is", self.stations[self.pos], self.name)
class AmState(State):
def __init__(self, radio):
self.radio = radio
self.stations = ["1250", "1380", "1510"]
self.pos = 0
self.name = "AM"
def toggle_amfm(self):
print("Switching to FM")
self.radio.state = self.radio.fmstate
class FmState(State):
def __init__(self, radio):
self.radio = radio
self.stations = ["81.3", "89.1", "103.9"]
self.pos = 0
self.name = "FM"
def toggle_amfm(self):
print("Switching to AM")
self.radio.state = self.radio.amstate
class Radio(object):
"""A radio. It has a scan button, and an AM/FM toggle switch."""
def __init__(self):
"""We have an AM state and an FM state"""
self.amstate = AmState(self)
self.fmstate = FmState(self)
self.state = self.amstate
def toggle_amfm(self):
self.state.toggle_amfm()
def scan(self):
self.state.scan()
# Test our radio out
if __name__ == '__main__':
radio = Radio()
actions = [radio.scan] * 2 + [radio.toggle_amfm] + [radio.scan] * 2
actions = actions * 2
for action in actions:
action()

22. Strategy( Strategy )

Back

Intention :

Define a set of algorithms , Encapsulate them one by one , And make them interchangeable . This pattern allows the algorithm to change independently of the customers using it .

Applicability :

Many of the related classes just behave differently .“ Strategy ” Provides a way to configure a class with one of several behaviors .

You need to use different variants of an algorithm . for example , You may define some spaces that reflect different / Time tradeoff algorithm . When these variants are implemented at the class level of an algorithm [H087] , You can use the strategy pattern .

The algorithm uses data that the customer should not know . You can use strategic patterns to avoid exposing complex 、 Data structure related to algorithm .

A class defines multiple behaviors , And these behaviors appear in the form of multiple conditional statements in the operation of this class . Move the relevant conditional branches into their respective Strategy Class to replace these conditional statements .

Realization :

#!/usr/bin/python
#coding:utf8
""" Strategy In most of other languages Strategy pattern is implemented via creating some base strategy interface/abstract class and subclassing it with a number of concrete strategies (as we can see at http://en.wikipedia.org/wiki/Strategy_pattern), however Python supports higher-order functions and allows us to have only one class and inject functions into it's instances, as shown in this example. """
import types
class StrategyExample:
def __init__(self, func=None):
self.name = 'Strategy Example 0'
if func is not None:
self.execute = types.MethodType(func, self)
def execute(self):
print(self.name)
def execute_replacement1(self):
print(self.name + ' from execute 1')
def execute_replacement2(self):
print(self.name + ' from execute 2')
if __name__ == '__main__':
strat0 = StrategyExample()
strat1 = StrategyExample(execute_replacement1)
strat1.name = 'Strategy Example 1'
strat2 = StrategyExample(execute_replacement2)
strat2.name = 'Strategy Example 2'
strat0.execute()
strat1.execute()
strat2.execute()

23. Visitor( The visitor )

Back

Intention :

Defines the skeleton of an algorithm in an operation , Instead, defer some steps to subclasses .TemplateMethod Allows subclasses to redefine certain steps of an algorithm without changing its structure .

Applicability :

Implement the invariant part of an algorithm at once , And leave the variable behavior to the subclass to implement .

The common behavior in each subclass should be extracted and concentrated in a common parent class to avoid code duplication . This is a Opdyke and Johnson Described “ Decomposing to generalize ” A good example of [OJ93]. First identify the differences in the existing code , And separate the differences into new operations . Last , Replace the different code with a template method that calls these new operations .

Control subclass extension . Template methods are only called at specific points “hook ” operation ( See the effects section ), This allows only extension at these points .

Realization :

#!/usr/bin/python
#coding:utf8
''' Visitor '''
class Node(object):
pass
class A(Node):
pass
class B(Node):
pass
class C(A, B):
pass
class Visitor(object):
def visit(self, node, *args, **kwargs):
meth = None
for cls in node.__class__.__mro__:
meth_name = 'visit_'+cls.__name__
meth = getattr(self, meth_name, None)
if meth:
break
if not meth:
meth = self.generic_visit
return meth(node, *args, **kwargs)
def generic_visit(self, node, *args, **kwargs):
print('generic_visit '+node.__class__.__name__)
def visit_B(self, node, *args, **kwargs):
print('visit_B '+node.__class__.__name__)
a = A()
b = B()
c = C()
visitor = Visitor()
visitor.visit(a)
visitor.visit(b)
visitor.visit(c)

above


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved