Skip to content

Commit 05b5ba9

Browse files
authored
Merge pull request pattu777#1 from faif/master
Pull 1
2 parents f7ec06d + 7e33a0d commit 05b5ba9

50 files changed

Lines changed: 2131 additions & 418 deletions

Some content is hidden

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

.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: 17 additions & 6 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()
@@ -59,14 +65,19 @@ def main():
5965

6066
### OUTPUT ###
6167
# PRODUCT LIST:
68+
# (Fetching from Data Store)
69+
# cheese
6270
# eggs
6371
# milk
64-
# cheese
65-
#
72+
#
73+
# (Fetching from Data Store)
6674
# PRODUCT INFORMATION:
6775
# Name: Cheese, Price: 2.00, Quantity: 10
76+
# (Fetching from Data Store)
6877
# PRODUCT INFORMATION:
6978
# Name: Eggs, Price: 0.20, Quantity: 100
79+
# (Fetching from Data Store)
7080
# PRODUCT INFORMATION:
7181
# Name: Milk, Price: 1.50, Quantity: 10
82+
# (Fetching from Data Store)
7283
# That product "arepas" does not exist in the records

README.md

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,63 @@
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.
55

66
When an implementation is added or modified, be sure to update this file and
7-
rerun `append_output.sh` to keep the output comments at the bottom up to date.
7+
rerun `append_output.sh` (eg. ./append_output.sh borg.py) to keep the output
8+
comments at the bottom up to date.
89

910
Current Patterns:
1011

11-
* 3-tier
12-
* abstract_factory
13-
* adapter
14-
* borg
15-
* bridge
16-
* builder
17-
* catalog
18-
* chain
19-
* command
20-
* composite
21-
* decorator
22-
* facade
23-
* factory_method
24-
* flyweight
25-
* graph_search
26-
* iterator
27-
* mediator
28-
* memento
29-
* mvc
30-
* null
31-
* observer
32-
* pool
33-
* prototype
34-
* proxy
35-
* publish_subscribe
36-
* state
37-
* strategy
38-
* template
39-
* visitor
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+
| [graph_search](graph_search.py) | (graphing algorithms, not design patterns) |

__init__.py

Whitespace-only changes.

abstract_factory.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,17 @@ class PetShop:
1313
"""A pet shop"""
1414

1515
def __init__(self, animal_factory=None):
16-
"""pet_factory is our abstract factory.
17-
We can set it at will."""
16+
"""pet_factory is our abstract factory. We can set it at will."""
1817

1918
self.pet_factory = animal_factory
2019

2120
def show_pet(self):
22-
"""Creates and shows a pet using the
23-
abstract factory"""
21+
"""Creates and shows a pet using the abstract factory"""
2422

2523
pet = self.pet_factory.get_pet()
26-
print("This is a lovely {}".format(pet))
24+
print("We have a lovely {}".format(pet))
2725
print("It says {}".format(pet.speak()))
28-
print("It eats {}".format(self.pet_factory.get_food()))
26+
print("We also have {}".format(self.pet_factory.get_food()))
2927

3028

3129
# Stuff that our factory makes
@@ -76,22 +74,21 @@ def get_factory():
7674

7775
# Show pets with various factories
7876
if __name__ == "__main__":
79-
shop = PetShop()
8077
for i in range(3):
81-
shop.pet_factory = get_factory()
78+
shop = PetShop(get_factory())
8279
shop.show_pet()
8380
print("=" * 20)
8481

8582
### OUTPUT ###
86-
# This is a lovely Dog
83+
# We have a lovely Dog
8784
# It says woof
88-
# It eats dog food
85+
# We also have dog food
8986
# ====================
90-
# This is a lovely Cat
91-
# It says meow
92-
# It eats cat food
93-
# ====================
94-
# This is a lovely Dog
87+
# We have a lovely Dog
9588
# It says woof
96-
# It eats dog food
89+
# We also have dog food
90+
# ====================
91+
# We have a lovely Cat
92+
# It says meow
93+
# We also have cat food
9794
# ====================

adapter.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33

44
"""http://ginstrom.com/scribbles/2008/11/06/generic-adapter-class-in-python/"""
55

6-
import os
7-
86

97
class Dog(object):
10-
118
def __init__(self):
129
self.name = "Dog"
1310

@@ -16,7 +13,6 @@ def bark(self):
1613

1714

1815
class Cat(object):
19-
2016
def __init__(self):
2117
self.name = "Cat"
2218

@@ -25,7 +21,6 @@ def meow(self):
2521

2622

2723
class Human(object):
28-
2924
def __init__(self):
3025
self.name = "Human"
3126

@@ -34,7 +29,6 @@ def speak(self):
3429

3530

3631
class Car(object):
37-
3832
def __init__(self):
3933
self.name = "Car"
4034

@@ -43,7 +37,6 @@ def make_noise(self, octane_level):
4337

4438

4539
class Adapter(object):
46-
4740
"""
4841
Adapts an object by replacing methods.
4942
Usage:
@@ -52,14 +45,18 @@ class Adapter(object):
5245
5346
>>> objects = []
5447
>>> dog = Dog()
55-
>>> objects.append(Adapter(dog, dict(make_noise=dog.bark)))
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'}
5653
>>> cat = Cat()
57-
>>> objects.append(Adapter(cat, dict(make_noise=cat.meow)))
54+
>>> objects.append(Adapter(cat, make_noise=cat.meow))
5855
>>> human = Human()
59-
>>> objects.append(Adapter(human, dict(make_noise=human.speak)))
56+
>>> objects.append(Adapter(human, make_noise=human.speak))
6057
>>> car = Car()
6158
>>> car_noise = lambda: car.make_noise(3)
62-
>>> objects.append(Adapter(car, dict(make_noise=car_noise)))
59+
>>> objects.append(Adapter(car, make_noise=car_noise))
6360
6461
>>> for obj in objects:
6562
... print('A {} goes {}'.format(obj.name, obj.make_noise()))
@@ -69,26 +66,32 @@ class Adapter(object):
6966
A Car goes vroom!!!
7067
"""
7168

72-
def __init__(self, obj, adapted_methods):
69+
def __init__(self, obj, **adapted_methods):
7370
"""We set the adapted methods in the object's dict"""
7471
self.obj = obj
7572
self.__dict__.update(adapted_methods)
7673

7774
def __getattr__(self, attr):
7875
"""All non-adapted calls are passed to the object"""
7976
return getattr(self.obj, attr)
80-
77+
78+
def original_dict(self):
79+
"""Print original object dict"""
80+
return self.obj.__dict__
8181

8282
def main():
8383
objects = []
8484
dog = Dog()
85-
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())
8689
cat = Cat()
87-
objects.append(Adapter(cat, dict(make_noise=cat.meow)))
90+
objects.append(Adapter(cat, make_noise=cat.meow))
8891
human = Human()
89-
objects.append(Adapter(human, dict(make_noise=human.speak)))
92+
objects.append(Adapter(human, make_noise=human.speak))
9093
car = Car()
91-
objects.append(Adapter(car, dict(make_noise=lambda: car.make_noise(3))))
94+
objects.append(Adapter(car, make_noise=lambda: car.make_noise(3)))
9295

9396
for obj in objects:
9497
print("A {0} goes {1}".format(obj.name, obj.make_noise()))
@@ -98,6 +101,9 @@ def main():
98101
main()
99102

100103
### 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'}
101107
# A Dog goes woof!
102108
# A Cat goes meow!
103109
# A Human goes 'hello'

append_output.sh

100644100755
File mode changed.

borg.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class Borg:
77

88
def __init__(self):
99
self.__dict__ = self.__shared_state
10+
self.state = 'Init'
1011

1112
def __str__(self):
1213
return self.state
@@ -44,8 +45,8 @@ class YourBorg(Borg):
4445
# rm2: Running
4546
# rm1: Zombie
4647
# rm2: Zombie
47-
# rm1 id: 139825262601040
48-
# rm2 id: 139825262601104
49-
# rm1: Zombie
50-
# rm2: Zombie
51-
# rm3: Zombie
48+
# rm1 id: 140732837899224
49+
# rm2 id: 140732837899296
50+
# rm1: Init
51+
# rm2: Init
52+
# rm3: Init

builder.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ def __init__(self):
3131
def new_building(self):
3232
self.building = Building()
3333

34+
def build_floor(self):
35+
raise NotImplementedError
36+
37+
def build_size(self):
38+
raise NotImplementedError
3439

3540
# Concrete Builder
3641
class BuilderHouse(Builder):

0 commit comments

Comments
 (0)