Professional Documents
Culture Documents
Who Am I
Came to Python via Zope in 1999. Worked at Digital Creations (aka Zope Corporation) until 2003. Now a consultant with Agendaless Consulting. Primary author of Pyramid web framework, Supervisor UNIX process control system, Deform form system, Repoze collection of middleware, and other unmentionables. Contributor to Zope, WebOb, and other OSS projects.
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
1/16
3/17/13
Guidelines
This talk covers four guidelines. If you follow these guidelines, your library will be useful for strangers in both the scenarios you expect and in ones you don't. The importance of the guidelines increases with the number of users whom might reuse your code and your social distance from those users.
Guidelines
#1: Global State is Precious #2: Don't Design Exclusively For Convenience #3: Avoid Knobs on Knobs #4: Composition Usually Beats Inheritance
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
2/16
3/17/13
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
3/16
3/17/13
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
4/16
3/17/13
Not-Really-Configuration
From the l o g g i n gpackage: # #r a i s e E x c e p t i o n si su s e dt os e ei fe x c e p t i o n sd u r i n g #h a n d l i n gs h o u l db ep r o p a g a t e d # r a i s e E x c e p t i o n s=1
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
5/16
3/17/13
OK at Module Scope
A non-circular import of another module or global. Assignment of a variable name in the module to some constant value. The addition of a function via a def statement. The addition of a class via a class statement. Control flow which handles conditionals for platform-specific failure handling of the above. Anything else will usually end in tears.
Solutions
Think of an application bootstrapping in two phases. Before i f_ _ n a m e _ _= =' _ _ m a i n _ _ ' : . Do nothing. As the result of i f_ _ n a m e _ _= =' _ _ m a i n _ _ ' . Do everything.
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
6/16
3/17/13
Downsides
Downside for m u l t i p r o c e s s i n gto ditch its global state maintenance: its API won't match that of t h r e a d i n g . Downside for l o g g i n g : streams related to the same handler might interleave. Downside for m i m e t y p e s : might need to reparse system mimetype files.
But, But..
You can always create library code such that it mutates no global state, but then, as necessary, create a convenience application module which integrates the library stuff and mutates some global state on behalf of its users. This makes the library code reusable, and if someone wants to use the application code, they can.
Quote
"This method of turning your code inside out is the secret to solving what appear to be hopelessly stateoriented problems in a purely functional style. Push the statefulness to a higher level and let the caller worry about it. Keep doing that as much as you can, and you'll end up with the bulk of the code being purely functional." -- http://prog21.dadgum.com/131.html
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
7/16
3/17/13
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
8/16
3/17/13
Instead
Design a framework so its users receive an argument (e.g. r e q u e s t ) and suggest to them that they pass derivations (e.g. r e q u e s t . G E T [ ' f i r s t _ n a m e ' ] ) around. It's less convenient for consumers. It's usually also the right thing to do in library and framework code. You can always create an (optional) convenience API that allows your library's consumers to elide the passing of state, but you can never remove a "mandatory" convenience feature from a library. Remember that people will want to use your stuff to compose larger systems, and your assumptions about environment may not fit there.
Convenience != Cleanliness
The assumption: "clean" == "is maximally convenient for the case I presume this code is going to be used in" The reality: "clean" == "maximally understandable; without surprises or exceptions". The fewer limiting assumptions made by the library, the fewer surprises it will have and the more understandable it will be. Ex: thread-local state management doesn't work in async systems without magical intervention.
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
9/16
3/17/13
Solution
Remove or hide a knob. Would probably be less confusing and more straightforward in this case to tell folks to subclass AuthTktAuthenticationPolicy and override e.g. a f i n d _ g r o u p smethod in the subclass instead of passing in a callback.
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
10/16
3/17/13
#4: Composing>Inheriting
Offering up superclasses "from on high" in a library or framework is often a bad idea. Composition usually beats inheritance (although not always).
Codependency
The "specialization interface" of a superclass can be hard to document and it's very easy to get wrong. Encapsulation is rarely honored when inheritance is used, so changes to a parent class will almost always break some number of existing subclasses whose implementers weren't paying attention to the specialization interface when they originally inherited from your library's superclass. The superclass may start simple, initially created to handle one or two particular cases of reuse via inheritance, but over time, as more folks add to its specialization interface, it will need to do more delegation, and may be required to become very abstract. When the superclass reaches a high level of abstraction, it may not be obvious what the purpose of the class is or why it's implemented as it is. A potential maintainer of the superclass may need to gain a detailed understanding of the implementation of in-the-wild subclasses in order to change the superclass. This can scare off potential contributors.
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html 11/16
3/17/13
Codependency (Cont'd)
The superclass may assume a particular state outcome from a combination of method calls. The expected outcome of such an operation is hard to explain and difficult for a subclass to enforce. It may need to change over time, breaking existing subclasses in hard-to-predict ways. Subclasses may unknowingly coopt and change the meaning of superclass instance or class variables.
Smells
From "Inheritance Considered Harmful" on http://www.midmarsh.co.uk Subclasses which override methods used by other inherited methods. A subclass which extends inherited methods using s u p e r . Other inherited methods may rely on the extended method. Subclass which uses or changes the state of "private" instance variables or calls/overrides methods not part of the specialization interface. [1] Which are thus reliant on the behaviour and results of the overridden methods.
Alternatives to Inheritance
Composition Event systems
Composition
Instead of telling folks to override a method of a library superclass via inheritance, you can tell them to pass in a component object to a library class constructor. The interaction between a component and your library code is "composition". When a library or framework uses a component, the only dependency between the library code and the component is the component's interface. A component represents the custom logic that would have otherwise gone in a method of a subclass. The library code will only have visibility into the component via its interface. The component needn't have any visibility into the library code at all (but often does). It's less likely that a component author will rely on non-API implementation details of the library than it would be if he was required to subclass a library parent class. The potential distraction of the ability to customize every aspect of the behavior of the system by overriding methods is removed. A clear contract makes it feasible to change the implementation of both the library and the component with reduced fear of breaking an integration of the two.
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
12/16
3/17/13
Inheritance Example
Here's an example of providing a class built to be specialized via inheritance: c l a s sT V R e m o t e ( o b j e c t ) : d e f_ _ i n i t _ _ ( s e l f ) : s e l f . c h a n n e l=0 d e fi n c r e m e n t _ c h a n n e l ( s e l f ) : s e l f . c h a n n e l+ =1 d e fc l i c k ( s e l f ,b u t t o n _ n a m e ) : r a i s eN o t I m p l e m e n t e d E r r o r
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
13/16
3/17/13
Composition (Cont'd)
Composition is "take it or leave it" customizability. It's a good choice when a problem and interaction is well-defined and well-understood (and, if you're writing a library for other people to use, this should, by definition, be true). But it can be limiting in requirements-sparse environments where the problem is not yet well-defined or well-understood. It can be easier to use inheritance in a system where you control the horizontal and vertical while you're working out exactly what the relationship between objects should be. If you control the horizontal and vertical, you can always later switch from inheritance to composition once the problem is fully understood and people begin to want to reuse your code.
Event Systems
Specialized kind of composition. For example, instead of adding a o n _ m o d i f i c a t i o nmethod of a class, and requiring that people subclass the class to override the method, have the would-be-superclass send an event to an event system. The event system can be subscribed to by system extenders as necessary.
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
14/16
3/17/13
Guidelines
#1: Global State is Precious #2: Don't Design Exclusively For Convenience #3: Avoid Knobs on Knobs #4: Composition Usually Beats Inheritance
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html 15/16
3/17/13
Contact Info
Chris McDonough, Agendaless Consulting @chrismcdonough on Twitter "mcdonc" on Freenode IRC
file://localhost/Volumes/Untitled/Uploaded/apidesign/presentation.html
16/16