Implementing Dependents and Dependencies in Lisp

Introduction

Here we implement, as a CLOS example, a common `design pattern'. Design patterns are an attempt to categorise and name solutions to commonly-occurring problems in object oriented (or general) software design.

Lisp has sometimes been criticised for its lack of support for such patterns. Partly this is because design patterns are less useful in a high level language with an advanced object system, and which allows syntactic extensions, such as Lisp. Partly it is because the implementation of these patterns is sufficiently easy in Lisp that people have often not formalised them. However there is a place for these ideas in Lisp, even if it is sometimes only to aid communication with people using other languages!

The classic book on design patterns is: Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides, `Design Patterns, Elements of Reusable Object-Oriented Software', Addison-Wesley, 1995.

Dependents and Dependencies

Here we implement (some of) the Observer pattern. It is a common requirement for objects to have `dependency' relations between them: if some object changes, it needs to tell all its dependents that it has changed so they can update themselves.

This pattern is very simple to implement given a notion of a collection class which can be `mixed in' to any other class. The dependents are just kept as members of the collection, and when the object is updated it must simply map over all its dependents and notify them.

Protocol for accessing dependents of objects

First, we need to define a protocol for objects with dependencies. This should contain aspects of the iteration protocol - you want to be able to map over the dependents of an object; and aspects of the collection protocol - you want to be able to add and delete dependents. For example, we could define at least:

A class for objects with dependents

Next, we meed to implelement a class which will represent an object with dependents. This should be quite easy. For reasons that will become apparent, we may not want to inherit from the collection class we defined earlier, but rather store an instance of it in a slot. We need to arrange that a suitable instance gets created when we make an instance of the dependents-class.

Protocol for tracking dependencies of objects which depend on other objects

As well as telling its dependents about something, an object may want to tell the objects that it depends on about something - for instance that it has `gone away' and they should stop notifying it about updates.

We need to design a protocol for this. It should be extremely similar to the protocol for objects with dependents, although with different function names. For example:

A class for objects which depend on other objects

Again this should be similar to the case for objects with dependents. Note that the reason we wished to store the instance of the collection class in a slot rather than inherit from it is because you may want to create classes which both have dependents and depend themselves, so two different collections may be associated with a given object - one for dependents and one for dependencies.

Making the two protocols work together

Now, we wish to modify the two protocols so that they work together, and implement the modifications.

When making an object which has dependencies be a dependent of an object with dependents, we need to make sure that both objects are informed of this. However if adding an object which does not have dependencies we do not (and should not) inform it. Similarly if an object which has dependencies is being given a new object to depend on, and that object can have dependents, we need to tell it. Similarly when removing dependents & dependencies.

We can do this by using CLOS's method combination and multiple-dispatch facilities: suitable {:after} methods on various of the generic functions in your protocol should do what we want. We need to be particularly careful about infinite recursion - you may need to add a secret argument which lets the protocol know if you have already recursed so it does not recurse further.

Authors: Gail Anderson (ga@cley.com), Tim Bradshaw(tfb@cley.com), Cley Limited.
Copyright 1999–2003 Cley Ltd. & Franz Inc.