Design Patterns in Python (Part 2) by Jeff Rush Jeff Rush <jeff@taupro.com> 1 of 13 Design Patterns in Python
What is a Pattern? a proven solution to a common problem in a specific context describes a problem which occurs over and over and then describes a core solution which can be applied repeatedly without doing it precisely the same way each time one person's pattern can be another person's primitive building block they are NOT about low-level elements such as linked lists or hash tables which can be encapsulated in a library and forgotten they are NOT about high-level elements such as domain-specific designs for an entire application or subsystem Jeff Rush <jeff@taupro.com> 2 of 13 Design Patterns in Python
Categorizing Patterns Gang-of-Four (GoF) book categorizes them by purpose : Creational instantiating objects Structural composing complex classes/objects from simpler ones Behavioral controlling ways in which classes/objects interact A pattern is often used together with other patterns. All patterns involve tradeoffs. Jeff Rush <jeff@taupro.com> 3 of 13 Design Patterns in Python
Purpose of Creational Patterns abstract the instantiation process are useful for systems that depend on object composition more than class inheritance shifts emphasis away from hardcoding a fixed set of behaviors toward defining a smaller set of primitive behaviors used to compose more complex ones tends to manage via hiding/abstraction two aspects: which concrete classes the system uses how instances of those classes are created and assembled Jeff Rush <jeff@taupro.com> 4 of 13 Design Patterns in Python
A Selection of Creational Patterns Factory Function/Method varies class of object that is created Abstract Factory varies entire families of product objects Builder varies, step-by-step, how a composite object gets created Prototype varies initial state of object that is created Singleton hands out the same object over and over Borg form of singleton but shares equality, not identity Jeff Rush <jeff@taupro.com> 5 of 13 Design Patterns in Python
Purpose of Structural Patterns concerned with how classes and objects are composed to form larger structures structural class patterns compose interfaces or implementations using for example, inheritance structural object patterns compose other objects to realize new functionality object patterns are more powerful because they permit composition changes at run-time Jeff Rush <jeff@taupro.com> 6 of 13 Design Patterns in Python
A Selection of Structural Patterns Adapter (make one object or class appear like another) Bridge (separate interface from implementation) Facade (provide a unified, simplified interface for multiple ones) Decorator Composite Flyweight Proxy Jeff Rush <jeff@taupro.com> 7 of 13 Design Patterns in Python
(Class) Adapter aka Wrapper (varies the interface of a class) enables reuse of a class by converting its interface into another interface clients expect. class LineShape(Shape): def boundingbox(self): return... class PolygonShape(Shape): def boundingbox(self): pass class TextShape(TextView): # adapts TextView into a Shape adapts existing TextView class into that of a Shape def init (self, color=none): TextView. init (self, textcolor=color) def boundingbox(self): return (self.getextent(0), self.getextent(1)) Inherits Shape's interface and TextView's implementation Relies upon inheritance and interface conventions. Less work for adapter if adaptee's interface is close to what client wants. Freezes the (one or more) interfaces provided by TextShape. Jeff Rush <jeff@taupro.com> 8 of 13 Design Patterns in Python
(Object) Adapter aka Wrapper (varies the interface of a class) enables reuse of an entire family of classes by converting its interface into another interface clients expect. class TextShape: adapts existing TextView class into that of a Shape def init (self, view): self.view = view # takes a 'delegate' def boundingbox(self): return (self.view.getextent(0), self.view.getextent(1)) Retains a reference to a TextView instance within a TextShape, implementing TextShape in terms of TextView's interface. Can adapt any subclass of View without requiring a corresponding Shape class. Relies upon principle of object composition, not inheritance. More work for adapter since it must catch and redirect all interface methods. Allows adaptee object to pair with more than one adaptor (facet pattern). Example: StringIO adapts an instance of 'string' to the 'filelike' interface. Jeff Rush <jeff@taupro.com> 9 of 13 Design Patterns in Python
Bridge aka Handle/Body decouples an abstraction from its implementation, so both can vary independently class BaseServer: pass class BaseRequestHandler: pass class HttpServer(BaseServer): def init (self, request_factory): self.request_factory = request_factory def connect(self, socket): return self.request_factory(socket) class HttpRequestHandler(BaseRequestHandler): pass class HttpsRequestHandler(BaseRequestHandler): pass server = HttpServer(HttpRequestHandler) Overcomes inflexibility of inheritance binding implementation to abstraction via: Creating dual class hierarchies; one for abstraction and one for implementation. Deferring binding of abstraction and implementation until run-time. Isolating clients from changes in implementation of abstraction. Used primarily when there are multiple implementations to choose from. Jeff Rush <jeff@taupro.com> 10 of 13 Design Patterns in Python
Facade aka Complexity Firewall reduce client complexity via a simplified, unified interface to a set of interfaces class Scanner: pass class Parser: pass class ProgramNodeBuilder: pass class CodeGenerator: pass class Compiler: scanner = Scanner parser = Parser nodebuilder = ProgramNodeBuilder generator = CodeGenerator def parse(self, filename): self.parser.parsefile(filename) cc = Compiler() cc.parse(filename) Reverses complexity of breaking down design into many small reusable parts. Reduces coupling of client to implementation pieces. Doesn't introduce new functionality, just makes the underlying easier to use. Doesn't store state and therefore is often a Singleton. Examples: Queue for list + lock or sets.set for dict Jeff Rush <jeff@taupro.com> 11 of 13 Design Patterns in Python
(Object) Decorator sneaks in the middle to attach additional responsibilities to instances dynamically def ucaser(wrapped_method): def replacement_write(self, data): wrapped_method(self, data.upper()) return replacement_write class ostream(file): @ucase def write(self, data): file.write(self, data) def write2(self, data): file.write(self, data) write2 = ucaser(write2) os = ostream('/var/tmp/junk.txt', 'w') os.write("hello World!") An alternative way to extend functionality w/o subclassing. Because of transparency, an unlimited number of layers can be attached. Python decorators encompass the Decorator pattern but can do more. Example: gzip.gzipfile decorates File with compression/decompression. Jeff Rush <jeff@taupro.com> 12 of 13 Design Patterns in Python
Wrapup A bridge is similar to an adapter, but an adapter is meant to change the interface of an existing object. A bridge separates interface from implementation. An adapter is geared to making unrelated classes work together. A bridge is used up-front in a design to let abstractions and implementations vary independently. A decorator enhances another object without changing its interface, unlike an adapter. A decorator is more transparent. Python Decorators PEP: http://www.python.org/peps/pep-0318.html Design Patterns talk by Alex Martelli: http://www.strakt.com/docs/ep04_pydp.pdf Jeff Rush <jeff@taupro.com> 13 of 13 Design Patterns in Python