Aggregation with Automatic Delegation

There’s a trend in object-oriented design to favor aggregation over inheritance. Allen Holub’s article “Why extends is evil” describes the problems with inheritance well.

The biggest problem with inheritance is it creates a tight coupling between a parent class and its descendant classes. We’re all taught back in Programming 101 to encapsulate implementation details into functions and methods with well defined interfaces. Later, we can change the implementation details without having to worry about breaking the rest of our code as long as we don’t change the interfaces.

But inheritance has a way of creeping in and destroying this encapsulation. So, you might change an implementation detail of a base class method without changing its interface or semantics at all, and in the process, you might badly break a subclass. In other words, you can’t safely change a base class without first understanding the impact of the change on all of its subclasses.

Put another way, inheritance reduces the agility of your code base.

Solving these problems requires a combination of interfaces, aggregation, and delegation. If you want to override a few methods in a given class C, C must implement an interface, and the code base must always reference the interface rather than the actual class. Create a new class C2 that implements the interface. Class C2 should not extend C; instead, C2 should contain a member variable of type C. Within C2, implement any of the methods that you want to override. For all other methods in the interface, you will need to create a one-line method in C2 that delegates to the member variable.

Stack Example in Java. Holub uses a stack class and a specialized stack class that records its high-water mark as an example in his article. Let’s build up his example in more detail using Java. First, we will create a Stack interface containing the typical, basic stack methods.

    public interface Stack<T> {
    	public int size();
    	public boolean empty();
    	public T peek();
    	public void push(T obj);
    	public void pushMultiple(T[] objs);
    	public T pop();
    }

Next, we’ll create a Java class StackImpl that implements the interface. As an aside, I always find it difficult to name classes and interfaces in these situations. It feels like both should be named Stack, but of course that isn’t possible. I could either call the interface Stack and the class a contrived name like StackImpl, or I could call the interface IStack and the class Stack. Since most of the code base should refer to the interface rather than the class, I prefer to give the contrived name to the class and the good name to the interface. My gut tells me that Java is probably mis-designed if it forces me to create contrived names like StackImpl, especially when other languages don’t have this problem. More on this later.

At any rate, here’s the code for class StackImpl:

    public class StackImpl<T> implements Stack<T> {

    	private List<T> stack;

    	public StackImpl() {
    		stack = new ArrayList<T>();
    	}

    	public boolean empty() {
    		return stack.isEmpty();
    	}

    	public T peek() {
    		return stack.get(0);
    	}

    	public T pop() {
    		return stack.remove(0);
    	}

    	public void push(T obj) {
    		stack.add(0, obj);
    	}

    	public void pushMultiple(T[] objs) {
    		for (T obj: objs) {
    			push(obj);
    		}
    	}

    	public int size() {
    		return stack.size();
    	}
    }

We want to create a class that behaves almost exactly like StackImpl. The only difference is that the new class will record the largest number of items that were ever on the stack at the same time. We’ll avoid inheritance and instead use aggregation to reduce coupling between StackImpl and our new class. While our first thought may be to jump in and start writing the new class, we will likely regret it later if we don’t create a new interface for the additional behavior:

    public interface HighWaterStack<T> extends Stack<T> {
    	public int getHighWaterMark();
    }

That is, a HighWaterStack is a Stack that provides an additional method for fetching the high-water mark. With the interface in place, we can now write the new class:

    public class HighWaterStackImpl<T> implements HighWaterStack<T> {

    	private int highWaterMark;
    	private Stack<T> stack;

    	public HighWaterStackImpl(Stack<T> stack) {
    		highWaterMark = 0;
    		this.stack = stack;
    	}

    	public int getHighWaterMark() {
    		return highWaterMark;
    	}

    	public boolean empty() {
    		return stack.empty();
    	}

    	public T peek() {
    		return stack.peek();
    	}

    	public T pop() {
    		return stack.pop();
    	}

    	public void push(T obj) {
    		stack.push(obj);
    		checkHighWaterMark();
    	}

    	public void pushMultiple(T[] objs) {
    		stack.pushMultiple(objs);
    		checkHighWaterMark();
    	}

    	public int size() {
    		return stack.size();
    	}

    	private void checkHighWaterMark() {
    		int stackSize = stack.size();
    		if (stackSize > highWaterMark) {
    			highWaterMark = stackSize;
    		}
    	}
    }

Class HighWaterStackImpl has a member variable which is a Stack. All stack methods except push() and pushMultiple() are delegated to the stack member variable. The two push methods delegate to the member variable and then check to see if the stack’s high-water mark has increased.

Aggregation Sure Makes Some Ugly Code! I cringe when I look at the code for HighWaterStackImpl. The problem is that we had to implement four methods (empty(), peek(), pop(), and size()) that simply delegate to the stack member. Of course, these methods weren’t that difficult to write, and they probably didn’t take much time to write. But typing in a bunch of stupid little methods like these is distracting to a developer. They break a developer’s concentration, cause him to lose his focus, and get him out of the zone. Lots of little distractions like these add up over time to significantly hurt productivity.

Perhaps more importantly, it’s difficult for a new developer to look at HighWaterStackImpl and quickly understand what it does. The problem is that the four stupid little delegating methods are all mixed in with the methods that have real logic. You have to wade through a bunch of cruft to find the real logic. (The getters and setters in Java beans cause the same problem.)

In this example, we’re only looking at overriding the implementation for one small interface. Imagine a typical system with hundreds of interfaces, some with substantial numbers of methods. We could quickly end up with huge numbers of cruft methods.

Of course, these problems would go away if we had used inheritance instead of aggregation. So, we’re forced with a tradeoff between tight coupling on the one hand and ugly code on the other hand.

Here’s what I wish Java would do: support automatic delegation. That is, if a class implements an interface and has a member variable that also implements the interface, I should be able to tell Java to delegate automatically to the member variable whenever the class is missing an interface method. Here’s an example of what HighWaterStackImpl might look like if Java supported automatic delegation (to be clear, the following is not valid Java):

    public class HighWaterStackImpl<T> implements HighWaterStack<T>[delegate to stack] {

    	private int highWaterMark;
    	private Stack<T> stack;

    	public HighWaterStackImpl(Stack<T> stack) {
    		highWaterMark = 0;
    		this.stack = stack;
    	}

    	public int getHighWaterMark() {
    		return highWaterMark;
    	}

    	public void push(T obj) {
    		stack.push(obj);
    		checkHighWaterMark();
    	}

    	public void pushMultiple(T[] objs) {
    		stack.pushMultiple(objs);
    		checkHighWaterMark();
    	}

    	private void checkHighWaterMark() {
    		int stackSize = stack.size();
    		if (stackSize > highWaterMark) {
    			highWaterMark = stackSize;
    		}
    	}
    }

All the cruft is gone, and we still have all the benefits of aggregation over inheritance. If languages supported automatic delegation, aggregation would be just as easy to use as inheritance, and it would be difficult to ever justify using inheritance again. Unfortunately, I’m not aware of any languages with built-in support for automatic delegation.

Automatic Delegation in Python. But it turns out that automatic delegation is pretty easy to implement in dynamic languages like Python. One approach is to use Python’s __getattr__() method. Method __getattr__() is called whenever code tries to access a member of an object and that member is not defined in the object’s dictionary. The normal __getattr__() behavior is to raise an AttributeError. However, you can override __getattr__() to do whatever you want, such as to delegate the member request to another object.

Here’s a Python class that demonstrates overriding __getattr__():

    class Delegator(object):
        def __init__(self, delegatees):
            self._delegatees = delegatees

        def __getattr__(self, name):
            for delegatee in self._delegatees:
                try:
                    return delegatee.__getattribute__(name)
                except AttributeError:
                    pass

            raise AttributeError('\'%s\' object has no attribute \'%s\'' % (self.__class__.__name__, name))

Instances of Delegator are constructed with a list of objects to delegate to. Whenever an undefined member is requested, Delegator goes through this list of delegatee objects attempting to delegate to each until successful. If the requested member cannot be delegated to any of the delegatees, then an AttributeError is raised. To use Delegator, simply inherit from it. (I know, it’s ironic that you have to inherit from Delegator in order to make it easier to use aggregation so that you can stop using inheritance. Sometimes life is funny.)

Here’s the complete stack example in Python using automatic delegation:

    class Stack(object):
        def __init__(self):
            self._stack = []

        def size(self):
            return len(self._stack)

        def empty(self):
            return self.size() == 0

        def peek(self):
            return self._stack[0]

        def push(self, obj):
            self._stack = [obj] + self._stack

        def push_multiple(self, obj_list):
            for obj in obj_list:
                self.push(obj)

        def pop(self):
            result = self._stack[0]
            self._stack = self._stack[1:]
            return result

    class HighWaterStack(Delegator):
        def __init__(self, stack):
            Delegator.__init__(self, [stack])
            self._stack = stack
            self.high_water_mark = 0

        def push(self, obj):
            self._stack.push(obj)
            self._check_high_water_mark()

        def push_multiple(self, obj_list):
            self._stack.push_multiple(obj_list)
            self._check_high_water_mark()

        def _check_high_water_mark(self):
            stack_size = self._stack.size()
            if stack_size > self.high_water_mark:
                self.high_water_mark = stack_size

All of the standard Stack methods can be called from HighWaterStack, even the ones such as pop() and peek() that aren’t explicitly defined on it or inherited into it. The following test code verifies that all methods are available:

    stack = HighWaterStack(Stack())
    stack.push(1)
    stack.push(2)
    stack.push(3)
    stack.pop()
    print stack.high_water_mark
    stack.push_multiple([4, 5, 6])
    print stack.high_water_mark

    while not stack.empty():
        print stack.pop(), stack.high_water_mark

Let’s compare the Python code using Delegator to the Java code. Notice that the Python code requires no interfaces and no contrived names such as StackImpl and HighWaterStackImpl. Nor does the Python code have any cruft methods. As in the Java code, there is still no inheritance between HighWaterStack and Stack, and an instance of HighWaterStack can be used in any context that expects a Stack.

Here are some metrics comparing the Python and Java code. I didn’t include blank lines in the line counts. Nor did I count the code for Delegator. Class Delegator should end up in a utility library and could potentially be reused many times in a large code base. Since it’s not specific to the stack problem, I didn’t count it.

    Java lines of code: 74
    Java lines of code, skipping ending braces: 52
    Java method implementations: 16

    Python lines of code: 33
    Python method implementations: 11

Conclusion. Aggregation with automatic delegation allows you to build clean code with the benefits of inheritance but none of the disadvantages. If you develop in Python, just add class Delegator to your toolbox, and you can say goodbye to inheritance forever. If you develop in another dynamically typed language, you can likely build automatic delegation code as well.

But if you code in a statically typed language such as Java, all you can do is plead with the language designers to add support for automatic delegation. Good luck. Until support is added, you’ll be left with a tough tradeoff between tightly coupled code using inheritance and ugly code using aggregation.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: