Saturday, September 15, 2007

Design Patterns in Python: II

As mentioned before, I am going through Part II of a talk on DPs in Python. The first part of the talk focuses on the Template Method design pattern. I have seen this pattern in the form of "algorithm" pattern in C++ template library and elsewhere (SAX parser library, various formatter libraries), but the video talk explains this very well. Alex Martelli offers "Self-Delgation" as an alternative name for this pattern. I suppose Algorithm could be another name, at least for some situations.

Well, we all know that the TM pattern concerns itself with separating the generic part of an algorithm or process into an "organizing method", which calls "hooks" at appropriate points to execute certain steps of the algorithm/process. So far, so good. What is revealing is that this separation can be done in three ways:

Inheritance TM

The organizing method is defined in an ABC. Sub-classes provide concrete implementations of hooks. Obvious variations: the parent class provides no-op, or reasonable default implementations of hooks. E.g., Queue.Queue follows the TM pattern and provides concrete hook implementations, which provide a full-implementation of a FIFO queue. However, because of the TM pattern, it can be sub-classed and the hooks overridden to make a LIFO queue, for e.g.

Factored TM

In this technique, the organizing method is in one class, and the hooks are implemented in another. The hook class can be passed to the class implementing the organizing method as an argument at instantiation time.

Further, hooks can be grouped. There can be a class per group.

Even further, each hook can be a class by itself. Then we get the Strategy pattern.

Mixin TM

This is applicable for languages allowing multiple inheritance. The idea is:

  • You multiply-inherit from a Mixin class that provides the "organizing methods"
  • You implement hooks to obtain the desired functionality.
An e.g.: you could derive your class from DictMixin, which causes your object to have the dictionary interface. All you have to do is implement a few hook methods, and automatically the rest of the rich dictionary interface (which is based on top of these hooks) is part of your class!

Combined Inheritance, Factored, and Mixin TM
One interesting use of these three techniques is in the SocketServer module:
  • TCPServer is a concrete implementations of network servers. They use factored TM: the TCP/UDPServer implements the organizing method, and a class derived from BaseRequestHandler provides the concrete hooks. Well, BaseRequestHandler provides some no-op hook implementations, that serve as documentation.
  • You can inherit from BaseRequestHandler and override some methods to specialize its behaviour. This is inheritance TM.
  • You can inherit from TCPServer and ThreadinMixin. ThreadingMixin overrides the TM of TCPServer and provides the multi-threaded TM. You just provide the hook implementation (the request handler) to have a multi-threaded tcp server.
The Strategy and State Patterns

The main idea in the Strategy Pattern seems to be: say you have a complicated process (the organizing method or OM) with many decision points. A decision point is impemented by an object which is invoked for action. That is, in TM we would have self.do_somthing(), but in Strategy we would have self.strat.do_something(). The strat is a reference to an object of some SC of a Strategy class that defines do_something(). To change the behaviour, we change strat from one SC instance to another. That is, we would do something like: self.strat = Strat1(), or self.strat = Strat2() depending on the situation. Obviously, Strat1 and Strat2 don't have to know much about each other for this to be scalable/appropriate.

In the State pattern, it is the state that knows how to switch behaviour. So we would have a decision point impemented as self.state.do_something(), and we would change behaviour by self.state.set_state(foo) or self.state.set_state(bar).

In Python, we can blur the lines:

1. Traditional

class Foo:
def bark():
print "bow"
class Bar:
def bark():
print "wow"
class OM:
def __init__():
self.strat = Foo()
def work():
print "working"
self.strat.bark()
print "work done"
def change_voice():
self.strat = Bar()

2. Change method

class OM:
def __init__():
def bark():
print "bow"
def bark2():
print "wow"
def work():
print "working"
self.bark()
print "work done"
def change_voice():
self.bark = self.bark2

3. Change __class__

class OM:
def work():
print "working"
self.bark()
print "work done"
def bark():
print "bow"
def change_voice():
self.__class__ = Cat

class Cat(OM):
def bark():
print "meow"

Design Patterns in Python: I

I found some time to catch up on general tech reading. Or rather, viewing. Because I have been viewing some excellent Google EngEDU videos.

I just watched "Advanced Topics in Programming Languages Series: Python Design Patterns (Part 1)". The presenter, Alex Martelli, emphasized that Design Patterns can be useful, useless or dangerous/overkill depending on the particular language of choice. In other words, as the A good example would be Singleton, which is probably conceptually broken. Python provides modules instead that work handily. I found myself remembering a particular use of Singleton in my own Python code that is, well, icky and reeks of uselessness. Anyway, Alex covered Creational Patterns in brief, and then went on to talk about Structural Patterns. They were more interesting and Alex gave some examples from the python library modules.

Inheritance vs Composition

Alex mentioned that object composition is favored over inheritance for most cases. By composing or "wrapping" we can do things that inheritance cannot, like selectively modifying the interface to an object, restricting the interface provided. (Inheritance cannot restrict the interface to the object).

Hold vs Wrap

So we go with composition. Composing objects leads to the choice of the "Hold vs Wrap" choice . I have faced this choice myself and I have seen the irritating consequences of the wrong choice.

Say I have to compose objects O with subobject S. Hold: object O has a reference to S, nothing more. Then I would access S as O.S.some_method(). Wrap: hold (maybe via a private name), and provide delegation, so that you can say O.some_method().

The latter choice brings us into the realm of the Structural Design Patterns. At this point I would say that it becomes quite irritating to see code like this: foo.bar.some_method(). Basically O is exposing its internal structure to its clients and violating the "Law of Demeter":http://en.wikipedia.org/wiki/Law_of_Demeter.

Creational Patterns

Alex discussed the Singleton pattern and how it has fundamentally a problem with inheritance. He clarified that instead of a Singleton, what is usually required is a shared-state. He introduced the Borg/Monostate pattern that solves the same problem with more control possible.

The next few patterns were the Factory series of patterns. Luckily, in Python, any callable can be passed into client code and can behave as an object factory or factory method. In fact, each class is a kind of factory, which can be customized to your heart's content by implementing/overriding the __new__() method.

Structural Patterns

The next few patterns to be discussed were:

  • Adapter: you have code expecting interface C, supplier code providing interface S (a superset of C), you write an adapter from C to S;
  • Facade: you have one or more objects; you create a single interface concentrating and simplifying the needed functionality for clients; a discussion of Facade was mentioned: http://www.tonymarston.net/php-mysql/design-patterns.html (I have not looked at it yet)
  • Bridge: you have several implementations of abstraction A, all expecting functionality F, and you have (or want to have) several implementations providing F, and you dont want the client code to know of/depend on the F implementation. To do this, make sure that each implementation of A holds a reference R to F, and accesses the functionality F only by delegating to R. A good example is the SocketServer module, where BaseServer is the abstraction A, BaseRequestHandler is the functionality F. The appropriate implementation of BaseRequestHandler is passed at creation time to the appropriate sub-class of BaseServer, which then keeps a reference to it (the R). When a new request handler is required, R() is called to create a new object. Alex mentions that this is rather like a factory (but then, callables are factories), and that it is typical of Bridge DPs in Python to keep reference to a class and instantiate from it later. I wonder what a bridge to do a ORM would look like. I am now a little bit more confident of digging into the guts of SQLAlchemy.
  • Decorator: reuse/tweak without inheritance. Difference from Adapter/Facade would be that the interface does not change, but new functionality is somehow added. A buffering adapter to a file object would be an example: the starting and resulting interface is the same, and the difference is only whether data is written to disk with or without buffering. The gzip module is an example.
  • Proxy: a proxy looks like a decorator but deals with lifetime, access, location issues rather than adding functionality. Hmm.. well, CORBA comes to mind.
I am now going to watch Part II of the same series, which should deal with Behavioral Patterns.

The slides used for the talk are available at http://www.aleax.it/goo_pydp.pdf (8 MB PDF file).