Design Patterns In Python

Transcription

Design Patterns in PythonAlex Martelli (aleax@google.com)http://www.aleax.it/gdd pydp.pdfCopyright 2007, Google Inc

The "levels" of this talkShuDP("Retain")HaPy("Detach")Ri("Transcend")2

Hit the ground running."Forces": some rich,complex subsystemoffers a lot of usefulfunctionality; clientcode interacts withseveral parts of thisfunctionality in a waythat's "out of control"this causes manyproblems for client-codeprogrammers ANDsubsystem ones too (complexity rigidity)3

Solution: the "Facade" DPinterpose a simpler"Facade" object/classexposing a controlledsubset of functionalityDP "Facade"client code now callsexisting supplier code ! provides ricinto the Facade, onlycomplex functionality in protocol Sthe Facade implements we need a simpler "subset" C of Sfacade code " implements and suppits simpler functionality (by calling S on !)via calls into the rich,complex subsystemsubsystem implementationgains flexibility, clients gain simplicity!!! 2004 AB Strakt417

Facade is a Design Patternsummary of a frequent design problem structure of a solution to that problem ( pros and cons, alternatives, .), and:A NAME (much easier to retain/discuss!)"descriptions of communicating objects andclasses customized to solve a general designproblem in a particular context"that's NOT: a data structure, algorithm,domain-specific system architecture,programming-language/library featureMUST be studied in a language's context!MUST supply Known Uses ("KU")5

Some Facade KUs.in the Python standard library.:dbhash facades for bsddbhighly simplified/subset accessalso meets the "dbm" interface (thus,also an example of the Adapter DP)os.path: basename, dirname facade forsplit indexing; isdir (&c) facade foros.stat stat.S ISDIR (&c)Facade is a structural DP (we'll see another,Adapter, later; in dbhash, they "merge"!-)6

Design Patterns7

What's a Design Patternsummary of a frequent design problem structure of a solution to that problem pros and cons, alternatives, ., and:A NAME (much easier to retain/discuss!)"descriptions of communicating objects andclasses customized to solve a general designproblem in a particular context"DPs are NOT: data structures, algorithms,domain-specific system architectures,programming language featuresMUST be studied in a language's context!MUST supply Known Uses ("KU")8

Many Good DP Books(biblio on the last slide)9

Classic DP CategoriesCreational: ways and means of objectinstantiationStructural: mutual composition of classes orobjects (the Facade DP is Structural)Behavioral: how classes or objects interactand distribute responsibilities among themEach can be class-level or object-level10

Prolegomena to DPs"program to an interface, not to animplementation"that's mostly done with "duck typing" inPython -- rarely w/"formal" interfacesactually similar to "signature-basedpolymorphism" in C templates11

Duck Typing Helps a Lot!Teaching the ducks to type takes a while,but saves you a lot of work afterwards!-)12

Prolegomena to DPs"favor object composition over classinheritance"in Python: hold, or wrapinherit only when it's really convenientexpose all methods in base class (reuse usually override maybe extend)but, it's a very strong coupling!13

Python: hold or wrap?14

Python: hold or wrap?“Hold”: object O has subobject S as anattribute (maybe property) -- that’s alluse self.S.method or O.S.methodsimple, direct, immediate, but. prettystrong coupling, often on the wrong axisholderholdeeclient15

Python: hold or wrap?“Wrap”: hold (often via private name) plusdelegation (so you directly use O.method)explicit (def method(self.).self.S.method)automatic (delegation in getattr )gets coupling right (Law of Demeter)wrapperwrappeeclient16

E.g: wrap to "restrict"class RestrictingWrapper(object):def init (self, w, block):self. w wself. block blockdef getattr (self, n):if n in self. block:raise AttributeError, nreturn getattr(self. w, n).Inheritance cannot restrict!17

Creational Patternsnot very common in Python.because "factory" is essentially built-in!-)18

Creational Patterns [1]"we want just one instance to exist"use a module instead of a classno subclassing, no special methods, .make just 1 instance (no enforcement)need to commit to "when" to make itsingleton ("highlander")subclassing not really smoothmonostate ("borg")Guido dislikes it19

Singleton ("Highlander")class Singleton(object):def new (cls, *a, **k):if not hasattr(cls, ' inst'):cls. inst super(Singleton, cls). new (cls, *a, **k)return cls. instsubclassing is a problem, though:class Foo(Singleton): passclass Bar(Foo): passf Foo(); b Bar(); # .?.problem is intrinsic to Singleton20

Monostate ("Borg")class Borg(object):shared state {}def new (cls, *a, **k):obj super(Borg, cls). new (cls, *a, **k)obj. dict cls. shared statereturn objsubclassing is no problem, just:class Foo(Borg): passclass Bar(Foo): passclass Baz(Foo): shared state {}data overriding to the rescue!21

Creational Patterns [2]"we don't want to commit to instantiating aspecific concrete class""Dependency Injection" DPno creation except "outside"what if multiple creations are needed?"Factory" subcategory of DPsmay create w/ever or reuse existingfactory functions (& other callables)factory methods (overridable)abstract factory classes22

Structural Patterns"Masquerading/Adaptation" subcategory:Adapter: tweak an interface (both class andobject variants exist)Facade: simplify a subsystem's interface.and many others I don't cover, such as:Bridge: many implementations of anabstraction, many implementations of afunctionality, no repetitive codingDecorator: reuse tweak w/o inheritanceProxy: decouple from access/location23

Adapterclient code γ requires a protocol Csupplier code σ provides different protocolS (with a superset of C's functionality)adapter code α "sneaks in the middle":to γ, α is a supplier (produces protocol C)to σ, α is a client (consumes protocol S)"inside", α implements C (by means ofappropriate calls to S on σ)24

Toy-example AdapterC requires method foobar(foo, bar)S supplies method barfoo(bar, foo)e.g., σ could be:class Barfooer(object):def barfoo(self, bar, foo):.25

Object Adapterper-instance, with wrapping delegation:class FoobarWrapper(object):def init (self, wrappee):self.w wrappeedef foobar(self, foo, bar):return self.w.barfoo(bar, foo)foobarer FoobarWrapper(barfooer)26

Class Adapter (direct)per-class, w/subclasing & self-delegation:class Foobarer(Barfooer):def foobar(self, foo, bar):return self.barfoo(bar, foo)foobarer Foobarer(.w/ever.)27

Class Adapter (mixin)flexible, good use of multiple inheritance:class BF2FB:def foobar(self, foo, bar):return self.barfoo(bar, foo)class Foobarer(BF2FB, Barfooer):passfoobarer Foobarer(.w/ever.)28

Adapter KUsocket. fileobject: from sockets to file-likeobjects (w/much code for buffering)doctest.DocTestSuite: adapts doctest teststo unittest.TestSuitedbhash: adapt bsddb to dbmStringIO: adapt str or unicode to file-likeshelve: adapt "limited dict" (str keys andvalues, basic methods) to complete mappingvia pickle for any - string UserDict.DictMixin29

Adapter observationssome RL adapters may require much codemixin classes are a great way to help adaptto rich protocols (implement advancedmethods on top of fundamental ones)Adapter occurs at all levels of complexityin Python, it's not just about classes andtheir instances (by a long shot!-) -- oftencallables are adapted (via decorators andother HOFs, closures, functools, .)30

Facade vs AdapterAdapter's about supplying a given protocolrequired by client-codeor, gain polymorphism via homogeneityFacade is about simplifying a rich interfacewhen just a subset is often neededFacade most often "fronts" for a subsystemmade up of many classes/objects, Adapter"front" for just one single object or class31

Behavioral PatternsTemplate Method: self-delegation."the essence of OOP".some of its many Python-specific variants32

Template Methodgreat pattern, lousy name"template" very overloadedgeneric programming in C generation of document from skeleton.a better name: self-delegationdirectly descriptive!-)33

Classic TMabstract base class offers "organizingmethod" which calls "hook methods"in ABC, hook methods stay abstractconcrete subclasses implement the hooksclient code calls organizing methodon some reference to ABC (injecter, or.)which of course refers to a concrete SC34

TM skeletonclass AbstractBase(object):def orgMethod(self):self.doThis()self.doThat()class Concrete(AbstractBase):def doThis(self): .def doThat(self): .35

KU: cmd.Cmd.cmdloopdef cmdloop(self):self.preloop()while True:s self.doinput()s self.precmd(s)finis self.docmd(s)finis self.postcmd(finis,s)if finis: breakself.postloop()36

Classic TM Rationalethe "organizing method" provides"structural logic" (sequencing &c)the "hook methods" perform "actual elementary'' actions"it's an often-appropriate factorization ofcommonality and variationfocuses on objects' (classes')responsibilities and collaborations: baseclass calls hooks, subclass supplies themapplies the "Hollywood Principle": "don'tcall us, we'll call you"37

A choice for hooksclass TheBase(object):def doThis(self):# provide a default (often a no-op)passdef doThat(self):# or, force subclass to implement# (might also just be missing.)raise NotImplementedErrorDefault implementations often handier, whensensible; but "mandatory" may be good docs.38

KU: Queue.Queueclass Queue:.def put(self, item):self.not full.acquire()try:while self. full():self.not full.wait()self. put(item)self.not empty.notify()finally:self.not full.release()def put(self, item): .39

Queue’s TMDPNot abstract, often used as-isthus, implements all hook-methodssubclass can customize queueing disciplinewith no worry about locking, timing, .default discipline is simple, useful FIFOcan override hook methods ( init, qsize,empty, full, put, get) AND.data (maxsize, queue), a Python special40

Customizing Queueclass LifoQueueA(Queue):def put(self, item):self.queue.appendleft(item)class LifoQueueB(Queue):def init(self, maxsize):self.maxsize maxsizeself.queue list()def get(self):return self.queue.pop()41

"Factoring out" the hooks"organizing method" in one class"hook methods" in anotherKU: HTML formatter vs writerKU: SAX parser vs handleradds one axis of variability/flexibilityshades towards the Strategy DP:Strategy: 1 abstract class per decisionpoint, independent concrete classesFactored TM: abstract/concrete classesmore "grouped"42

TM introspection"organizing" class can snoop into "hook"class (maybe descendant) at runtimefind out what hook methods existdispatch appropriately (including "catchall" and/or other error-handling)43

KU: cmd.Cmd.docmddef docmd(self, cmd, a):.try:fn getattr(self, 'do ' cmd)except AttributeError:return self.dodefault(cmd, a)return fn(a)44

Questions & AnswersQ?A!45

1.Design Patterns: Elements of Reusable Object-Oriented Software -Gamma, Helms, Johnson, Vlissides -- advanced, very deep, THE classic"Gang of 4" book that started it all (C )2.Head First Design Patterns -- Freeman -- introductory, fast-paced,very hands-on (Java)3.Design Patterns Explained -- Shalloway, Trott -- introductory, mixof examples, reasoning and explanation (Java)4.The Design Patterns Smalltalk Companion -- Alpert, Brown, Woolf-- intermediate, very language-specific (Smalltalk)5.Agile Software Development, Principles, Patterns and Practices -Martin -- intermediate, extremely practical, great mix of theory andpractice (Java, C )6.Refactoring to Patterns -- Kerievsky -- introductory, strongemphasis on refactoring existing code (Java)7.Pattern Hatching, Design Patterns Applied -- Vlissides -- advanced,anecdotal, specific applications of idea from the Gof4 book (C )8.Modern C Design: Generic Programming and Design PatternsApplied -- Alexandrescu -- advanced, very language specific (C )46

6.Refactoring to Patterns -- Kerievsky -- introductory, strong emphasis on refactoring existing code (Java) 7.Pattern Hatching, Design Patterns Applied -- Vlissides -- advanced, anecdotal, specific applications of idea from the Gof4 book (C ) 8.Modern C Design: Generic Programming and Design Patterns