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 .
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))
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)
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)
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()
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
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()
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()
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);
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()
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()
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))
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 :
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()
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)
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()
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()
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()
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 " "
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()
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)
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()
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()
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()
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
List of articles Preface Tit
Why study python Mainstream pr