A little disappointed so far

Jay O'Connor joconnor at nets.com
Mon May 19 01:12:58 EDT 2003


In article <LKXxa.1217$573.151 at news-binary.blueyonder.co.uk>, "Graham
Nicholls" <graham at rockcons.co.uk> wrote:

> Jay O'Connor wrote:
> >For example, rather
>> than testing a shape if it is a cricle, and then drawing a circle, or a
>> square, etc...yuo simple call 'draw()' on the shape and let
>> polymorphism handle that the correct version of draw() for cicles,
>> squares, etc.. is called (I acn post a code exmple if you would like).

> If its not too much trouble...

OK, consider the following code.  I have shapes, I want to draw them.
Each shape knows what type it is.  I test for the shape type and draw my
shape (just print here)

class Shape:

    def __init__(self, aType = 'shape'):
        self.shapeType=aType

def drawShape(aShape):
    
    if (aShape.shapeType == 'circle'):
        print "circle"
        return
    
    if (aShape.shapeType == 'square'):
        print "square"
        return

    print "shape"

drawShape(Shape('circle'))
drawShape(Shape('square'))


So the function tests the shape type and then draws the shape
accordingly.

The problem with this is that Shapes don't do much, but the real problem
is that adding new shapes requires chnages to the clientcode that uses
the shape(consider functions life areaOfShape() etc..that have to be
written simularly.

So what we will do is use a bit of inheritance and polymorphism to our
advantage
class Shape:
        
    def draw (self):
        print "shape"


class Circle (Shape):
        
    def draw (self):
        print "circle"

class Square(Shape):
       
    def draw (self):
        print "circle"

def drawShape(aShape):
    aShape.draw()

drawShape(Circle())
drawShape(Square())
drawShape(Shape())


As you can see, our drawShape() method is much simpler, plus we can add
shapes easily without having to rewrite drawShape() (and areaOfShape(),
etc...) so our code is more resiliant to change, and eash piece of code
is smaller and easier to understand.


>> An often used part of this a technique called double dispatch (where
>> when you call to the object, in a general way, it calls back to you in
>> a specify way..again I can provide a code example)

> ditto...

The classic example is in math when you want to deal with math operations
between mixed types.

a =1
b =1.1
a + b

Now, as is an integer and b is a float, and at a low level, adding an
integer and a float is not the same as adding two integers, etc..

You can solve this with 'double dispatch' which basically means I have an
operation on two different types and I need to resolve down to a specific
routine depending on what both of them are.  This can be done with nested
case or if-else structruesm, or it can be done with polymorphism again.

 Assuming I want to draw shapes on different medium
(screen, printer, etc...) but the routing for drawing a given shape on
the screen is different than on the printer (example here...probably not
realistic..) I have an object representing soem canvas and an object
representing some shape and I need to use the right routine to draw that
shape on that canvas

class Shape:

    def drawOn (self, aCanvas):
        aCanvas.drawGenericShape(self)

class Circle (Shape):

    def drawOn (self, aCanvas):
        aCanvas.drawCircle(self)

class Square(Shape):
        
    def drawOn (self, aCanvas):
        aCanvas.drawSquare(self)


class Canvas:
    
    def drawShape(self, aShape):
        aShape.drawOn(self)

class ScreenCanvas (Canvas):
    
    def drawCircle (self, aCircle):
        print "draw circle on the screen"

    def drawSquare (self, aSquare):
        print "draw square on the screen"

    def drawGenericShape (self, aShape):
        print "draw something on the screen"


class PrinterCanvas(Canvas):
    
    def drawCircle (self, aCircle):
        print "draw circle on the printer"

    def drawSquare (self, aSquare):
        print "draw square on the printer"

    def drawGenericShape (self, aShape):
        print "draw something on the printer"
   

#set up some collections of devices and shapes

devices = [ScreenCanvas(), PrinterCanvas()]
shapes = [Square(), Circle()]

#assume the use asked to print a collection of shapes
c = devices[1]
for shape in shapes:
    c.drawShape(shape)

#assume the use asked to display a collection of shapes
c = devices[0]
for shape in shapes:
    c.drawShape(shape)

=====output=====
draw square on the printer
draw circle on the printer
draw square on the screen
draw circle on the screen


so in my loop, I have a canvas, and a set of shapes.  Here I could put a
big nested if-case type of statement to resolve to a particular
function to call, but instead I just tell canvas 'here's a shape...draw
it' by calling c.drawShape(shape)

So the canvas has something to draw, but has to know what it is.  Again,
a case statement could be used here, but instead I'm going to defer of to
the object passed in and tell it "draw yourself, on me" with
aShape.drawOn(self).  So the circle know's it a circle, but doesn't
really know what type of canvas it's been given, so it just says "I know
I'm a circle, I'll tell the canvas to draw a circle on itself', by
calling "aCanvas.drawCircle(self)"  That's the double-displath.  The call
to the first object gets refocused to the second object with some
information narrowing the search, so to speak.  The second object can in
turn call back to the first in such a way that the exact operation has
been pinpointed

This is a contrived example that shows the mechnism, but that's about it.
 How you can have two sets of classes  of objects with varying instances of
those subclasses, and resolve to the proper operation between them,
without ever using  a case statement, or even an if statement.  The
simple structure of the classes and polymorphism resolve everything out.
It can be very powerful when the objects yuo are acting on and the
objects you are acting with are in different subsystems, or they can
evolve independantly.

-- 
Jay O'Connor
http://www.r4h-music.com

"God Himself plays the bass strings first,
when He tunes the soul"




More information about the Python-list mailing list