1. Code Reuse with Multiple Decorators?

In this tutorial, we're going to look at how we can use forwarding decorators to enable code reuse when we have a large decorator structure.

The decorator pattern enables us to enrich the existing functionality. A decorator implements an interface and also wraps another implementation. Assume that we have an interface with multiple methods, like java.util.List. So the decorators must implement all the methods adding new functionality only to some of them. If we need additional functionality for a small subset of the methods, the other methods just delegate the operation to the wrapped implementation. If multiple decorators present the same behavior, this leads to code duplication.

2. Forwarding Decorator

Let's look at the details of implementing the forwarding decorator. Conceptually, we'll combine the decorator pattern with inheritance. Firstly, we must define a base decorator class that forwards all calls to the wrapped instance. This base class doesn't add new functionality. Then all other decorators - of the given interface - must extend this base class and override the required methods to add new functionality.

We'll start with the Animal interface:

public interface Animal {

    void walk();

    void run();

    void eat(String meal);

    Animal breed(Animal animal);

    void sleep();
}

Animal contains five methods for the subclasses to implement.

Next, we'll provide an implementation:

public class Dog implements Animal {

    @Override
    public void walk() {
        System.out.println("Dog is walking.");
    }

    @Override
    public void run() {
        System.out.println("Dog is running.");
    }

    @Override
    public void eat(String meal) {
        System.out.println("Dog is eating.");
    }

    @Override
    public Animal breed(Animal animal) {
        System.out.println("Dog is breeding.");
        return new Dog();
    }

    @Override
    public void sleep() {
        System.out.println("Dog is sleeping.");
    }
}

Here, we have the Dog class. Note that it isn't a decorator and makes no use of delegation. For our purposes, we'll use it to do the actual work.

Then we'll define the base decorator, the ForwardingAnimal class. As we mentioned previously, it just forwards the calls.

public abstract class ForwardingAnimal implements Animal {

    private final Animal delegate;

    public ForwardingAnimal(Animal delegate) {
        this.delegate = delegate;
    }

    @Override
    public void walk() {
        delegate.walk();
    }

    @Override
    public void run() {
        delegate.run();
    }

    @Override
    public void eat(String meal) {
        delegate.eat(meal);
    }

    @Override
    public Animal breed(Animal animal) {
        return animal.breed(animal);
    }

    @Override
    public void sleep() {
        delegate.sleep();
    }
}

An important point is that ForwardingAnimal is abstract and can't be instantiated.

By using the base decorator, we'll build a decorator that counts the breeding occurrences:

public class BreedingAwareDecorator extends ForwardingAnimal {

    private final AtomicInteger breedingCount = new AtomicInteger();

    public BreedingAwareDecorator(Animal animal) {
        super(animal);
    }

    @Override
    public Animal breed(Animal animal) {
        Animal baby = super.breed(animal);
        System.out.println("Breeding count: " + breedingCount.incrementAndGet());
        return baby;
    }
}

Here, BreedingAwareDecorator extends ForwardingAnimal and just overrides the breed method for the counting functionality. Since the superclass has the delegation already in place, we don't need to implement other methods. Here lies the main motive of the forwarding decorators. The more decorators we create by extending the ForwardingAnimal class, the more benefit we'll gain because of the code reuse.

Lastly, we have a small application to test the usage:

public static void main(String[] args) {
    Animal dogDecorator = new BreedingAwareDecorator(new Dog());
    dogDecorator.breed(new Dog());
    dogDecorator.breed(new Dog());
    dogDecorator.breed(new Dog());
}

A sample run prints:

Dog is breeding.
Breeding count: 1
Dog is breeding.
Breeding count: 2
Dog is breeding.
Breeding count: 3

3. Real-World Examples

The Google Guava library has forwarding decorator implementations like ForwardingList, ForwardingMap, and ForwardingSet.

4. Summary

In this tutorial, we've looked at how we can create a forwarding decorator to reduce code duplication when we have multiple decorators implementing a large interface.

As always, the source code for all examples in this tutorial is available on Github.