Octopodial Chrome

Stuff that Made Sense at the Time

The Personal Weblog of Bob Uhl


Thursday, 13 March 2008

When All You Have is a Hammer...

…screws don’t make much sense.

We’re all familiar with the adage that says when all you have is a hammer, everything looks like a nail. That’s true enough as far as it goes. We tend to frame problems in terms of what we know. However, there’s an even more pernicious effect: we don’t recognise the utility of solutions outside our experience. If all you have is a hammer, screws seem like a distinctly sub-optimal type of nail: they require more force to hammer in, they damage the wood and they don’t hold as well. Once you have a screwdriver, you realise that screws are actually superior to nails for many tasks.

The article inheritance is evil and must be destroyed is written by someone who only knows about hammers. He uses the example of a class representing a ball; it’s further subclassed into bouncing balls and balls which fade in and out. But what if one wants to have a bouncing, fading ball? He writes using inheritance, the code that handles bouncing and fading is locked up in the BouncingBall and FadingBall classes and can’t be used elsewhere. Well, in a language which only allows single inheritance, sure: you can only inherit from one superclass, and so it’s not possible to have a bouncing fading ball which inherits from both bouncing and fading balls.

Fortunately, we’ve had a well-defined object system since the late 1980s. It allows multiple inheritance, and all one has to do is inherit behaviour from both superclasses and Everything Works. An example:

(defclass ball ()
  ((x
    :accessor x
    :initform 0
    :initarg :x)
   (y
    :accessor y
    :initform 0
    :initarg :y)
   (colour
    :accessor colour
    :initform 'white 
    :initarg :colour)))

(defclass bouncing-ball (ball)
  ((elasticity
    :accessor elasticity
    :initform 1
    :initarg :elasticity)
   vector))

(defclass fading-ball (ball)
  ((fade-rate
    :accessor fade-rate
    :initform 1
    :initarg :fade-rate)))

(defclass fading-bouncing-ball (fading-ball bouncing-ball) ())

(defgeneric draw (ball)
  (:documentation "Draw BALL at its position"))

(defmethod draw ((ball ball))
  "Draw BALL at its position, with the proper colour."
  (draw-circle (x ball) (y ball)) (colour ball))

(defmethod draw :before ((ball bouncing-ball))
  "Move BALL to the next position as it bounces."
  (with-slots (vector x y) ball
    (setf x (bounce-x x vector)
          y (bounce-y y vector))))

(defmethod colour :around ((ball fading-ball))
  "Return BALL’s colour faded for this point in time."
  (fade-colour (call-next-method ball) (fade-rate ball) (get-current-time)))

And yes, everything works exactly as expected. The DRAW method specialised on balls draws a circle wherever it needs to; the DRAW before-method specialised on bouncing balls runs before a bouncing ball is drawn; it updates its current position and then the primary DRAW method is called, drawing the ball at the new position. The COLOUR around-method specialised on fading balls calls the normal COLOUR method, figures out the faded colour and then returns it. Yes, an instance of FADING-BOUNCING-BALL inherits from both FADING-BALL and BOUNCING-BALL, and they both inherit from BALL. It all just works.

You can either keep on trying to hammer in screws, or you can learn how to use a screwdriver, or you can claim that screws are awful fasteners and that nails are always better. One of these is a better solution than the others.


March
Sun Mon Tue Wed Thu Fri Sat
           
13
         
2008
Months
Mar
Dec

Powered by Blosxom | Subscribe with Bloglines | Listed on
BlogShares | Blogarama - The Blog Directory | Technorati Profile

This is my blogchalk:
United States, Colorado, Englewood, Centennial, English, , Robert, Male, 21–25, Free Software, Society for Creative Anachronism.