Wednesday, March 21, 2012

Polymorphism: Beyond the Factory Method

So here's the basic idea. There is some situation, where you have several or many similar classes. Let's say we have customers. They are broken into a bunch of categories, based on buying habits, geographic location, blah blah blah.

Faced with this situation, it's a clear case for inheritance. Common attributes are set in the base class, and specific attributes and/or method overrides are set in the subclasses. A frequent approach to this is to build a factory for building these, which can recognise from the initialisation arguments and maybe some context what kind of customer to build in each situation.

That's all fine and great, but I'd like to propose a new approach: doing all that inside the base class constructor. I initialise a base class Customer with all the relevant info. I get back a customer, but maybe I get back an AustralianCustomer or a FilthyRichCustomer.

Here's an example of how to do that in Python.


class BasicClass(object):
  def __new__(cls, value, category):
    if category == 1:
       return CatOne.__new__(CatOne, value, category)
    if category == 2:
       return CatTwo.__new__(CatTwo, value, category)

class CatOne(BasicClass):
 
   def __new__(cls, value, category):
      instance = object.__new__(CatOne)
      #instance.__class__ = CatOne
      return instance
   def __init__(self, value, category):
      self.value = "CatOne: %s " % value

class CatTwo(BasicClass):
   def __new__(cls, value, category):
     instance = object.__new__(CatTwo)
     return instance
   def __init__(self, value, category):
      self.value = "CatTwo: %s" % value

foo = BasicClass("Hello", 1)
bar = BasicClass("World", 2)
print foo
print foo.value


What's going on here is that the category classes *do* inherit from BasicClass. Any methods I put onto BasicClass will get inherited down the stack. However, when I initialise it, what I actually get back is one of the category classes. I have effectively pushed the factory pattern into the base classe's constructor methods in order to simplify how I create objects.

I think I like it :)