About me

Feeds

Object-oriented programming

How do you explain the benefits of object-oriented programming to a beginner without getting into the details of CLOS?

Here's my attempt to come up with a short compelling example showing how object-oriented programming simplifies the writing of programs in Lisp:

Introduction

Object-oriented programming allows you to organise programs in a more logical way. Instead of having a single program which operates on an inert mass of data, the data itself is told how to behave, and the program is implicit in the interactions of these new data “objects.”

For example, suppose we have two types of objects, rectangles and circles, and the functions rectangle-p and circle-p to test what type an object is.

Now suppose we want to write a program to find the areas of two-dimensional shapes.

The old non-object-oriented way to do this would be to write a single function which looked at the type of its argument and behaved accordingly. So, to find the area of an object x we would do:

Methods

The object-oriented approach is to make each object able to calculate its own area. The area function is broken apart and a different version is provided for each type of object. These different versions of a function are called methods.

In Lisp methods are defined using defmethod, rather than defun. So the equivalent object-oriented definitions for area might be:

Like defun, in defmethod the function name is followed by a list of arguments, but any argument can be a list:

(argumenttype)

to specify that the function is only called if the argument is of that type.

When we call:

(area obj)

the correct method will automatically be chosen by CLOS according to what type of object obj is.

Classes

In object-oriented programming we define the type of each object using classes. In our simple example we have just two types of object, rectangles and circles. We could define these as follows:

(defclass rectangle ())
(defclass circle ())

The argument of defclass is the class that this class inherits from. In their most general form classes can form a hierarchy, like a tree, with the most general classes at the top and the more specialised classes further down. So we might make all the classes used by our program inherit from a shape class, and we might include square as a special case of rectangle:

The defclass definition can also specify slots, which are variables associated with each instance of an object. For example, our rectangle objects could have a height and width, and our circle objects could have a radius:

Making objects

Finally, when we want to make a new object we use make-instance to make an instance of the appropriate class, a bit like peeling a sheet off a pad of identical forms:

(setq myrect (make-instance 'rectangle :width 24 :height 10))

(Note that we would have to make the defclass definitions a bit more complicated for this to work as shown in practice).

Each instance of rectangle has its own separate width and height slots.

Now we can do:

> (area myrect)

and we'll get:

240

Summary

Why is the object-oriented approach better than the simple approach at the start of this section, using defun and a cond to check the different possible types of object?

The object-oriented approach keeps the information about how to deal with each type of object in the method for that type of object, rather than having it in one large function that has to deal with all types of object.

If at a later date you want to make your program handle a new type of object you simply have to add an appropriate class and methods, without needing to edit your existing methods.

Slots are a convenient way of storing all the information about particular instances of a class.

Because classes are hierarchical you can provide a single method that works on several classes in the hierarchy. For example, as defined above area will automatically work on objects of type square because they are a subclass of rectangle.

Full example

Here's a full working example based on the methods and classes described above. You can run this in LispWorks to see how it works.

Suppose we are a manufacturer of perspex shapes for children.

First we define the classes. The perspex comes in different thicknesses, so we give the shape class a thickness slot: