Skip to content

Commit ab9de61

Browse files
committed
added specification design pattern.
1 parent fa2bc83 commit ab9de61

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Current Patterns:
3737
| [prototype](prototype.py) | use a factory and clones of a prototype for new instances (if instantiation is expensive) |
3838
| [proxy](proxy.py) | an object funnels operations to something else |
3939
| [publish_subscribe](publish_subscribe.py) | a source syndicates events/data to 0+ registered listeners |
40+
| [specification](specification.py) | business rules can be recombined by chaining the business rules together using boolean logic |
4041
| [state](state.py) | logic is org'd into a discrete number of potential states and the next state that can be transitioned to |
4142
| [strategy](strategy.py) | selectable operations over the same data |
4243
| [template](template.py) | an object imposes a structure but takes pluggable components |

specification.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
@author: Gordeev Andrey <gordeev.and.and@gmail.com>
6+
7+
Specification provide recombination business logic by
8+
chaining together using boolean logic
9+
"""
10+
11+
from abc import ABCMeta, abstractmethod
12+
13+
14+
class Specification:
15+
__metaclass__ = ABCMeta
16+
17+
def and_specification(self, candidate):
18+
raise NotImplementedError
19+
20+
def or_specification(self, candidate):
21+
raise NotImplementedError
22+
23+
def not_specification(self):
24+
raise NotImplementedError
25+
26+
@abstractmethod
27+
def is_satisfied_by(self, candidate):
28+
pass
29+
30+
31+
class CompositeSpecification(Specification):
32+
@abstractmethod
33+
def is_satisfied_by(self, candidate):
34+
pass
35+
36+
def and_specification(self, candidate):
37+
return AndSpecification(self, candidate)
38+
39+
def or_specification(self, candidate):
40+
return OrSpecification(self, candidate)
41+
42+
def not_specification(self):
43+
return NotSpecification(self)
44+
45+
46+
class AndSpecification(CompositeSpecification):
47+
_one = Specification()
48+
_other = Specification()
49+
50+
def __init__(self, one, other):
51+
self._one = one
52+
self._other = other
53+
54+
def is_satisfied_by(self, candidate):
55+
return bool(self._one.is_satisfied_by(candidate) and
56+
self._other.is_satisfied_by(candidate))
57+
58+
59+
class OrSpecification(CompositeSpecification):
60+
_one = Specification()
61+
_other = Specification()
62+
63+
def __init__(self, one, other):
64+
self._one = one
65+
self._other = other
66+
67+
def is_satisfied_by(self, candidate):
68+
return bool(self._one.is_satisfied_by(candidate) or
69+
self._other.is_satisfied_by(candidate))
70+
71+
72+
class NotSpecification(CompositeSpecification):
73+
_wrapped = Specification()
74+
75+
def __init__(self, wrapped):
76+
self._wrapped = wrapped
77+
78+
def is_satisfied_by(self, candidate):
79+
return bool(not self._wrapped.is_satisfied_by(candidate))
80+
81+
82+
class User(object):
83+
84+
def __init__(self, super_user=False):
85+
self.super_user = super_user
86+
87+
88+
class UserSpecification(CompositeSpecification):
89+
90+
def is_satisfied_by(self, candidate):
91+
return isinstance(candidate, User)
92+
93+
94+
class SuperUserSpecification(CompositeSpecification):
95+
96+
def is_satisfied_by(self, candidate):
97+
return getattr(candidate, 'super_user', False)
98+
99+
100+
if __name__ == '__main__':
101+
print('Specification')
102+
andrey = User()
103+
ivan = User(super_user=True)
104+
105+
root_specification = UserSpecification().\
106+
and_specification(SuperUserSpecification())
107+
108+
print(root_specification.is_satisfied_by(andrey))
109+
print(root_specification.is_satisfied_by(ivan))
110+

0 commit comments

Comments
 (0)