Skip to content

Commit c7b14a0

Browse files
authored
Merge pull request #1 from faif/master
Pull from upstream.
2 parents d207051 + 8498a66 commit c7b14a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2933
-421
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__

.travis.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# vim ft=yaml
2+
# travis-ci.org definition for python-patterns build
3+
language: python
4+
5+
sudo: false
6+
7+
python:
8+
- "2.7"
9+
- "3.3"
10+
- "3.4"
11+
- "3.5"
12+
# Disabled for now since cause more pain than gain
13+
# - "pypy"
14+
# - "pypy3"
15+
16+
cache:
17+
- pip
18+
19+
install:
20+
- travis_retry pip install -q coveralls codecov
21+
- pip install flake8 # eventually worth
22+
23+
script:
24+
# Run tests
25+
- PYTHONPATH=. nosetests -s -v --with-doctest --with-cov --cover-package . --logging-level=INFO -v .
26+
# Actually run all the scripts, contributing to coverage
27+
- ./run_all.sh
28+
# for now failure in flaking is ignored
29+
- flake8 *py || echo "PEP8 the code"
30+
31+
after_success:
32+
- coveralls
33+
- codecov

3-tier.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,33 @@
33

44

55
class Data(object):
6+
""" Data Store Class """
67

78
products = {
89
'milk': {'price': 1.50, 'quantity': 10},
910
'eggs': {'price': 0.20, 'quantity': 100},
1011
'cheese': {'price': 2.00, 'quantity': 10}
1112
}
1213

14+
def __get__(self, obj, klas):
15+
print("(Fetching from Data Store)")
16+
return {'products': self.products}
17+
1318

1419
class BusinessLogic(object):
20+
""" Business logic holding data store instances """
1521

16-
def __init__(self):
17-
self.data = Data()
22+
data = Data()
1823

1924
def product_list(self):
20-
return self.data.products.keys()
25+
return self.data['products'].keys()
2126

2227
def product_information(self, product):
23-
return self.data.products.get(product, None)
28+
return self.data['products'].get(product, None)
2429

2530

2631
class Ui(object):
32+
""" UI interaction class """
2733

2834
def __init__(self):
2935
self.business_logic = BusinessLogic()
@@ -36,20 +42,42 @@ def get_product_list(self):
3642

3743
def get_product_information(self, product):
3844
product_info = self.business_logic.product_information(product)
39-
if product_info is not None:
45+
if product_info:
4046
print('PRODUCT INFORMATION:')
41-
print('Name: %s, Price: %.2f, Quantity: %d\n' %
42-
(product.title(), product_info.get('price', 0),
43-
product_info.get('quantity', 0)))
47+
print('Name: {0}, Price: {1:.2f}, Quantity: {2:}'.format(
48+
product.title(), product_info.get('price', 0),
49+
product_info.get('quantity', 0)))
4450
else:
45-
print('That product "%s" does not exist in the records' % product)
46-
51+
print('That product "{0}" does not exist in the records'.format(
52+
product))
4753

48-
if __name__ == '__main__':
4954

55+
def main():
5056
ui = Ui()
5157
ui.get_product_list()
5258
ui.get_product_information('cheese')
5359
ui.get_product_information('eggs')
5460
ui.get_product_information('milk')
5561
ui.get_product_information('arepas')
62+
63+
if __name__ == '__main__':
64+
main()
65+
66+
### OUTPUT ###
67+
# PRODUCT LIST:
68+
# (Fetching from Data Store)
69+
# cheese
70+
# eggs
71+
# milk
72+
#
73+
# (Fetching from Data Store)
74+
# PRODUCT INFORMATION:
75+
# Name: Cheese, Price: 2.00, Quantity: 10
76+
# (Fetching from Data Store)
77+
# PRODUCT INFORMATION:
78+
# Name: Eggs, Price: 0.20, Quantity: 100
79+
# (Fetching from Data Store)
80+
# PRODUCT INFORMATION:
81+
# Name: Milk, Price: 1.50, Quantity: 10
82+
# (Fetching from Data Store)
83+
# That product "arepas" does not exist in the records

README.md

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,64 @@
11
python-patterns
22
===============
33

4-
A collection of design patterns implemented (by other people) in python
4+
A collection of design patterns and idioms in Python.
5+
6+
When an implementation is added or modified, be sure to update this file and
7+
rerun `append_output.sh` (eg. ./append_output.sh borg.py) to keep the output
8+
comments at the bottom up to date.
9+
10+
Current Patterns:
11+
12+
__Creational Patterns__:
13+
14+
| Pattern | Description |
15+
|:-------:| ----------- |
16+
| [abstract_factory](abstract_factory.py) | use a generic function with specific factories |
17+
| [borg](borg.py) | a singleton with shared-state among instances |
18+
| [builder](builder.py) | instead of using multiple constructors, builder object receives parameters and returns constructed objects |
19+
| [factory_method](factory_method.py) | delegate a specialized function/method to create instances |
20+
| [lazy_evaluation](lazy_evaluation.py) | lazily-evaluated property pattern in Python |
21+
| [pool](pool.py) | preinstantiate and maintain a group of instances of the same type |
22+
| [prototype](prototype.py) | use a factory and clones of a prototype for new instances (if instantiation is expensive) |
23+
24+
__Structural Patterns__:
25+
26+
| Pattern | Description |
27+
|:-------:| ----------- |
28+
| [3-tier](3-tier.py) | data<->business logic<->presentation separation (strict relationships) |
29+
| [adapter](adapter.py) | adapt one interface to another using a white-list |
30+
| [bridge](bridge.py) | a client-provider middleman to soften interface changes |
31+
| [composite](composite.py) | encapsulate and provide access to a number of different objects |
32+
| [decorator](decorator.py) | wrap functionality with other functionality in order to affect outputs |
33+
| [facade](facade.py) | use one class as an API to a number of others |
34+
| [flyweight](flyweight.py) | transparently reuse existing instances of objects with similar/identical state |
35+
| [front_controller](front_controller.py) | single handler requests coming to the application |
36+
| [mvc](mvc.py) | model<->view<->controller (non-strict relationships) |
37+
| [proxy](proxy.py) | an object funnels operations to something else |
38+
39+
__Behavioral Patterns__:
40+
41+
| Pattern | Description |
42+
|:-------:| ----------- |
43+
| [chain](chain.py) | apply a chain of successive handlers to try and process the data |
44+
| [catalog](catalog.py) | general methods will call different specialized methods based on construction parameter |
45+
| [chaining_method](chaining_method.py) | continue callback next object method |
46+
| [command](command.py) | bundle a command and arguments to call later |
47+
| [mediator](mediator.py) | an object that knows how to connect other objects and act as a proxy |
48+
| [memento](memento.py) | generate an opaque token that can be used to go back to a previous state |
49+
| [observer](observer.py) | provide a callback for notification of events/changes to data |
50+
| [publish_subscribe](publish_subscribe.py) | a source syndicates events/data to 0+ registered listeners |
51+
| [registry](registry.py) | keep track of all subclasses of a given class |
52+
| [specification](specification.py) | business rules can be recombined by chaining the business rules together using boolean logic |
53+
| [state](state.py) | logic is organized into a discrete number of potential states and the next state that can be transitioned to |
54+
| [strategy](strategy.py) | selectable operations over the same data |
55+
| [template](template.py) | an object imposes a structure but takes pluggable components |
56+
| [visitor](visitor.py) | invoke a callback for all items of a collection |
57+
58+
59+
__Others__:
60+
61+
| Pattern | Description |
62+
|:-------:| ----------- |
63+
| [blackboard](blackboard.py) | (architectural model, assemble different sub-system knowledge to build a solution, AI approach - non gang of four pattern) |
64+
| [graph_search](graph_search.py) | (graphing algorithms, not design patterns) |

__init__.py

Whitespace-only changes.

__pycache__/facade.cpython-33.pyc

-3.51 KB
Binary file not shown.
-6.83 KB
Binary file not shown.

__pycache__/null.cpython-33.pyc

-2.85 KB
Binary file not shown.

abstract_factory.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
14
# http://ginstrom.com/scribbles/2007/10/08/design-patterns-python-style/
25

36
"""Implementation of the abstract factory pattern"""
@@ -6,27 +9,27 @@
69

710

811
class PetShop:
12+
913
"""A pet shop"""
1014

1115
def __init__(self, animal_factory=None):
12-
"""pet_factory is our abstract factory.
13-
We can set it at will."""
16+
"""pet_factory is our abstract factory. We can set it at will."""
1417

1518
self.pet_factory = animal_factory
1619

1720
def show_pet(self):
18-
"""Creates and shows a pet using the
19-
abstract factory"""
21+
"""Creates and shows a pet using the abstract factory"""
2022

2123
pet = self.pet_factory.get_pet()
22-
print("This is a lovely", pet)
23-
print("It says", pet.speak())
24-
print("It eats", self.pet_factory.get_food())
24+
print("We have a lovely {}".format(pet))
25+
print("It says {}".format(pet.speak()))
26+
print("We also have {}".format(self.pet_factory.get_food()))
2527

2628

2729
# Stuff that our factory makes
2830

2931
class Dog:
32+
3033
def speak(self):
3134
return "woof"
3235

@@ -35,6 +38,7 @@ def __str__(self):
3538

3639

3740
class Cat:
41+
3842
def speak(self):
3943
return "meow"
4044

@@ -45,6 +49,7 @@ def __str__(self):
4549
# Factory classes
4650

4751
class DogFactory:
52+
4853
def get_pet(self):
4954
return Dog()
5055

@@ -53,6 +58,7 @@ def get_food(self):
5358

5459

5560
class CatFactory:
61+
5662
def get_pet(self):
5763
return Cat()
5864

@@ -68,8 +74,21 @@ def get_factory():
6874

6975
# Show pets with various factories
7076
if __name__ == "__main__":
71-
shop = PetShop()
7277
for i in range(3):
73-
shop.pet_factory = get_factory()
78+
shop = PetShop(get_factory())
7479
shop.show_pet()
7580
print("=" * 20)
81+
82+
### OUTPUT ###
83+
# We have a lovely Dog
84+
# It says woof
85+
# We also have dog food
86+
# ====================
87+
# We have a lovely Dog
88+
# It says woof
89+
# We also have dog food
90+
# ====================
91+
# We have a lovely Cat
92+
# It says meow
93+
# We also have cat food
94+
# ====================

adapter.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# http://ginstrom.com/scribbles/2008/11/06/generic-adapter-class-in-python/
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
23

3-
import os
4+
"""http://ginstrom.com/scribbles/2008/11/06/generic-adapter-class-in-python/"""
45

56

67
class Dog(object):
@@ -32,7 +33,7 @@ def __init__(self):
3233
self.name = "Car"
3334

3435
def make_noise(self, octane_level):
35-
return "vroom%s" % ("!" * octane_level)
36+
return "vroom{0}".format("!" * octane_level)
3637

3738

3839
class Adapter(object):
@@ -41,32 +42,69 @@ class Adapter(object):
4142
Usage:
4243
dog = Dog
4344
dog = Adapter(dog, dict(make_noise=dog.bark))
45+
46+
>>> objects = []
47+
>>> dog = Dog()
48+
>>> print(dog.__dict__)
49+
{'name': 'Dog'}
50+
>>> objects.append(Adapter(dog, make_noise=dog.bark))
51+
>>> print(objects[0].original_dict())
52+
{'name': 'Dog'}
53+
>>> cat = Cat()
54+
>>> objects.append(Adapter(cat, make_noise=cat.meow))
55+
>>> human = Human()
56+
>>> objects.append(Adapter(human, make_noise=human.speak))
57+
>>> car = Car()
58+
>>> car_noise = lambda: car.make_noise(3)
59+
>>> objects.append(Adapter(car, make_noise=car_noise))
60+
61+
>>> for obj in objects:
62+
... print('A {} goes {}'.format(obj.name, obj.make_noise()))
63+
A Dog goes woof!
64+
A Cat goes meow!
65+
A Human goes 'hello'
66+
A Car goes vroom!!!
4467
"""
45-
def __init__(self, obj, adapted_methods):
68+
69+
def __init__(self, obj, **adapted_methods):
4670
"""We set the adapted methods in the object's dict"""
4771
self.obj = obj
4872
self.__dict__.update(adapted_methods)
4973

5074
def __getattr__(self, attr):
5175
"""All non-adapted calls are passed to the object"""
5276
return getattr(self.obj, attr)
53-
77+
78+
def original_dict(self):
79+
"""Print original object dict"""
80+
return self.obj.__dict__
5481

5582
def main():
5683
objects = []
5784
dog = Dog()
58-
objects.append(Adapter(dog, dict(make_noise=dog.bark)))
85+
print(dog.__dict__)
86+
objects.append(Adapter(dog, make_noise=dog.bark))
87+
print(objects[0].__dict__)
88+
print(objects[0].original_dict())
5989
cat = Cat()
60-
objects.append(Adapter(cat, dict(make_noise=cat.meow)))
90+
objects.append(Adapter(cat, make_noise=cat.meow))
6191
human = Human()
62-
objects.append(Adapter(human, dict(make_noise=human.speak)))
92+
objects.append(Adapter(human, make_noise=human.speak))
6393
car = Car()
64-
car_noise = lambda: car.make_noise(3)
65-
objects.append(Adapter(car, dict(make_noise=car_noise)))
94+
objects.append(Adapter(car, make_noise=lambda: car.make_noise(3)))
6695

6796
for obj in objects:
68-
print("A", obj.name, "goes", obj.make_noise())
97+
print("A {0} goes {1}".format(obj.name, obj.make_noise()))
6998

7099

71100
if __name__ == "__main__":
72101
main()
102+
103+
### OUTPUT ###
104+
# {'name': 'Dog'}
105+
# {'make_noise': <bound method Dog.bark of <__main__.Dog object at 0x7f631ba3fb00>>, 'obj': <__main__.Dog object at 0x7f631ba3fb00>}
106+
# {'name': 'Dog'}
107+
# A Dog goes woof!
108+
# A Cat goes meow!
109+
# A Human goes 'hello'
110+
# A Car goes vroom!!!

0 commit comments

Comments
 (0)