forked from oxnz/design-patterns
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add more impls in python: abstract-factory, decorator, facade, factor…
…y-method, iterator, flyweight, memento, null-object, observer, template
- Loading branch information
oxnz
committed
Sep 26, 2013
1 parent
fd0d2e2
commit 986b8c8
Showing
12 changed files
with
625 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#!/usr/bin/python | ||
#coding: UTF-8 | ||
|
||
"""Implementation of the abstract factory pattern""" | ||
|
||
from abc import abstractmethod | ||
|
||
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 "Pet: %s says [%s] while eating [%s]" % (pet, pet.speak(), self.pet_factory.get_food()) | ||
|
||
# Stuff that our factory makes | ||
|
||
class Pet: | ||
@abstractmethod | ||
def speack(self): | ||
pass | ||
@abstractmethod | ||
def __str__(self): | ||
return "" | ||
|
||
class Dog(Pet): | ||
def speak(self): | ||
return "woof!" | ||
def __str__(self): | ||
return "Dog" | ||
|
||
class Cat(Pet): | ||
def speak(self): | ||
return "meow~" | ||
def __str__(self): | ||
return "Cat" | ||
|
||
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" | ||
|
||
# Creates the proper family | ||
def get_factory(): | ||
"""Let's be dynamic!""" | ||
import random | ||
return random.choice([DogFactory, CatFactory])() | ||
|
||
if __name__ == '__main__': | ||
# Show pets with various factories | ||
shop = PetShop() | ||
for i in range(3): | ||
shop.pet_factory = get_factory() | ||
shop.show_pet() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#!/usr/bin/env python | ||
#-*- coding: utf-8 -*- | ||
|
||
class Foo(object): | ||
def f1(self): | ||
print ("original f1") | ||
def f2(self, x): | ||
print ("original f2: %s") % x | ||
|
||
class DecoratedFoo(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) | ||
|
||
if __name__ == '__main__': | ||
f = Foo() | ||
df = DecoratedFoo(f) | ||
df.f1() | ||
f.f2('x') | ||
df.f2('xyz') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# coding: utf-8 | ||
|
||
""" | ||
This one is another gimme — decorators have built-in syntax since Python 2.4 (they're still trivial to implement in earlier versions). | ||
This example shows a time_this decorator, which measures and prints the time it takes the decorated function to run, in seconds. | ||
""" | ||
|
||
import time | ||
|
||
def time_this(func): | ||
"""The time_this decorator""" | ||
|
||
def decorated(*args, **kwargs): | ||
start = time.time() | ||
result = func(*args, **kwargs) | ||
print "Ran in", time.time() - start, "seconds" | ||
return result | ||
return decorated | ||
|
||
# Decorator syntax | ||
@time_this | ||
def count(until): | ||
"""Count to 'until', then returns the result""" | ||
|
||
print "Counting to", until, "..." | ||
num = 0 | ||
for i in xrange(to_num(until)): | ||
num += 1 | ||
return num | ||
|
||
def to_num(numstr): | ||
"""Turns a comma-separated number string to an int""" | ||
return int(numstr.replace(",", "")) | ||
|
||
# Run count with various values | ||
def test(): | ||
for number in ("10,000", "100,000", "1,000,000"): | ||
print count(number) | ||
print "-" * 20 | ||
|
||
if __name__ == '__main__': | ||
test() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#coding: utf-8 | ||
|
||
class KillVirus: | ||
def operation(self): | ||
print "killing virus" | ||
|
||
class ZipFile: | ||
def operation(self): | ||
print "zipping" | ||
|
||
class EncryptFile: | ||
def operation(self): | ||
print "encrypting" | ||
|
||
# Facade | ||
class Filesys: | ||
def __init__(self): | ||
self._kv = KillVirus() | ||
self._zf = ZipFile() | ||
self._ef = EncryptFile() | ||
|
||
def operation(self): | ||
"""Wrapped all details in this function""" | ||
[i.operation() for i in (self._kv, self._zf, self._ef)] | ||
|
||
if __name__ == '__main__': | ||
fs = Filesys() | ||
fs.operation() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#coding: utf-8 | ||
|
||
class EnglishGetter: | ||
'''simple echoes the msg''' | ||
def get(self, msg): | ||
return unicode(msg) | ||
|
||
class JapaneseGetter: | ||
'''A simple localizer a la gettext''' | ||
|
||
def __init__(self): | ||
self.trans = dict(dog="犬", cat="猫") | ||
|
||
def get(self, msg): | ||
'''we'll punt if we don't have a traslation''' | ||
try: | ||
return unicode(self.trans[msg], "utf-8") | ||
except KeyError: | ||
return unicode(msg) | ||
|
||
def get_localizer(language="English"): | ||
'''The factory method''' | ||
languages = dict(English=EnglishGetter, Japanese=JapaneseGetter) | ||
return languages[language]() | ||
|
||
# Create our localizers | ||
e, j = get_localizer('English'), get_localizer('Japanese') | ||
|
||
# Localize some text | ||
for msg in "dog parrot cat".split(): | ||
print e.get(msg), j.get(msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#coding: utf-8 | ||
|
||
'''http://codesnipers.com/?q=python-flyweights''' | ||
|
||
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 = obj.__new__(cls) | ||
obj.value, obj.suit = value, suit | ||
Card._CardPool[value+suit] = obj | ||
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#coding: utf-8 | ||
'''Implementation of the iterator pattern with a generator | ||
http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/ | ||
''' | ||
|
||
def count_to(count): | ||
'''Counts by work numbers, up to a maximum of five''' | ||
numbers = ["one", "two", "three", "four", "five"] | ||
# The zip keeps from counting over the limit | ||
for number, pos in zip(numbers, range(count)): | ||
'''yield可以用来为一个函数返回值塞数据''' | ||
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 '\nCounting to five...' | ||
for number in count_to_five(): | ||
print number, | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#!/usr/bin/env python | ||
#-*- coding: utf-8 -*- | ||
|
||
""" | ||
Memento pattern | ||
http://code.activestate.com/recipes/413838-memento-closure/ | ||
The memento pattern is great for transaction-like processing. Having a handy implementation around might not be the worst thing. | ||
""" | ||
|
||
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 realy 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 state in self.states: | ||
state() | ||
|
||
class transactional: | ||
"""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 | ||
|
||
if __name__ == '__main__': | ||
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 | ||
|
||
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 |
Oops, something went wrong.