[Tutor] Re: Factory classes (etc)

Magnus Lycka magnus@thinkware.se
Wed, 04 Sep 2002 12:15:36 +0200


At 01:32 2002-09-04 +0200, Scot W. Stevenson wrote:
>The term itself is out of a book called "Design Patterns: Elements of
>Reusuable Object-Orientated Software" by four gentlemen named Gamma, Helm,
>Johnson and Vlissides.

Actually, the book "Design Patterns" (also called "the Gang of Four book"
or GoF) describes the patterns "Abstract Factory" (or Kit) and "Factory
Method" (Virtual Constructor). Not Factory Class. Googling for "factory
pattern" will yield plenty of hits. The term "Factory Pattern" also floats
around. See for instance
http://java.sun.com/products/jdk/1.2/docs/guide/rmi/Factory.html .
Sun writes: ``A factory, in this context, is a piece of software that
implements one of the "factory" design patterns introduced in the book,
Design Patterns, Elements of Reusable Object-Oriented Software.=B4=B4 Then
they fail miserably in describing either pattern. :( Their "real world"
examples describe the concept of any function or subroutine. It has
nothing to do with any factory pattern. Sigh.

I guess the problem is that the Factory patterns aren't based on any
real world problem, but on a pure consequence of how object oriented
programming languages are typically implemented: You typically get hold
of an object by invoking a constructor method in a class object. If you
want to be able to get instances of different classes depending on
context, this won't work. You can't write:

myVehicle =3D Car() or AirPlane() or Boat() or Rocket() depending on how
             and where I travel;

Neither Java nor C++ accepts that. Syntax error! So you write:

myVehicle =3D getVehicle()

and the Factory Method getVehicle has to figure out what vehicle you
need, depending on some kind of state. Maybe you pass an origin and
a destination as parameters. (Maybe budget too. :)

Your spray can example is neither an "Abstract Factory" nor a "Factory
Method" I'm afraid. Both the "Abstract Factory" and "Factory Method"
patterns return instances of _different_classes_ depending on the situation.
The issue is not to pass parameters, but rather to instantiate different
stuff depending on the situation. In your case you might as well call
spraycan's constructor directly. The point is that if instances of different
classes might be returned, you can't call a constructor directly.

# Factory function for symmetrical shapes
def getSymmetricalShape(x, y, size, corners=3D0, angle=3D0):
     assert type(corners) =3D=3D type(1) and (corners =3D=3D 0 or corners >=
 1)
     if corners =3D=3D 0:
         return Circle(x, y, size)
     elif corners =3D=3D 2:
         return Line(x, y, size, angle)
     elif corners =3D=3D 3:
         return Triangle(x, y, size, angle)
     elif corners =3D=3D 4:
         return Square(x, y, size, angle)
     else:
         return RegularPolygon(x, y, size, sides, angle)

for i in range(7):
     if i =3D=3D 1: continue
     shape =3D getSymmetricalShape(i*5, i*10, i*3+5, i)
     shape.fill(red)
     shape.paint()

GoFs Factory Method Pattern goes beyond this factory function in that
it wraps it in a class. Then you can subclass that class and have a
slightly different factory method there. But that is of less relevance
in Python than in C++ or Java. Inheriting is not as important in Python.

An Abstract Factory provides an interface for creating families of related
or dependent objects without specifying their concrete classes. For=
 instance,
this might be useful in cross platform GUI toolkits. You write some code
which works "everywhere" and "under the hood" different classes are=20
instantiated
depending on what underlying GUI toolkit you use on a particular platform.

A Factory Method is similar, but it only deals with one "product" which can
be of different classes, not a whole "family" such as in the GUI toolkit
case. An Abstract Factory might will use Factory Methods, but Factory=
 Methods
can also be useful in other contexts.

See http://www.c2.com/cgi/wiki?search=3DFactory for more info. (BTW,
the Portland Pattern Repository Wiki http://www.c2.com/cgi/wiki is a
great place to learn about programming.)

See also http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/86900 etc.

>It seems to be required reading for Real Computer
>Scientists: Stand next to one at a cocktail party for long enough, and
>sooner or later, they'll start talking about "the gang of four"...

Oh no. Real Computer Scientists don't go to cocktail parties! ;)

It's not a bad book, but since Python is so dynamic, you can often solve
the problems they try to solve with their pattern in a simpler way.
To some extent GoF can be read as an example of how difficult life gets
when you code in C++ or Java. This is a bit strange, since the Pattern
movement (in software design) started among SmallTalkers, and SmallTalk
is dynamically typed--I don't know enough SmallTalk to explain this. You
typically use inheritance less to achieve polymorphism in python than in
static languages such as Java and C++. In python, polymorphism doesn't
require use of inheritance, and inheritance is no guarantee that=
 polymorphism
will work since there is no static type checking. In python inheritance is
used more to avoid duplication of code.

An example:

class Blue:
     def whatAmI(self):
         print "I'm cool blue"

class Red:
     def whatAmI(self):
         print "I'm hot red"

b =3D Blue()
r =3D Red()

for c in [b, r]: c.whatAmI()

This will work, but if you change to:

for c in [b, r, "Woops!"]: c.whatAmI()

you will get a run time error.

If we imagine that Python had static type checking similar to C++ or
Java, you would have to do something strange like this:

class Color:
     virtual def whatAmI(self)

class Blue(Color):
     def whatAmI(self):
         print "I'm cool blue"

class Red(Color):
     def whatAmI(self):
         print "I'm hot red"

Blue b =3D Blue()
Blue r =3D Red()

for Color c in (Color)[b, r]: c.whatAmI()

The compiler would like to check at compile time that nothing
bad like the "Woops!" above would occur. It does this by making
sure that c is of a class that provides the method whatAmI, that
all objects that might be referred to by the variable c are
instantiated by Color or a Color subclass (must be a subclass
in this case since color is virtual) and by checking that the
concrete Color subclasses actually implement whatAmI. It would
also check that parameter lists for the method calls are compatible
in the inheritance chain.

In this simple example it's fairly obvious that it's easier for
the programmer to check the validity of the program himself than
write all the extra code that will make it possible for a compiler
to check it. In larger systems this is disputed. Maybe most people
favour static type checking. It eliminated a lot of simple (but
possibly dangerous) errors, and it restrains programmers. (Whether
that is good or bad depends on your perspective.) But it also
makes the code bloated and makes some types of convenient operations
very difficult. It will lead to duplication of code, extra concepts
such as templates, interfaces and more complex inheritance structures.

I'm not saying that the ideas in GoF are overly complex, but I think
it would have been a much thinner book if the Gang of Four had used
Python...


--=20
Magnus Lyck=E5, Thinkware AB
=C4lvans v=E4g 99, SE-907 50 UME=C5
tel: 070-582 80 65, fax: 070-612 80 65
http://www.thinkware.se/  mailto:magnus@thinkware.se