Object-oriented – Using the Decorator pattern to add public methods to an object

designdesign-patternsobject-oriented

The Decorator pattern is usually used to extend the functionality of an object by extending one of it's current methods.

To illustrate, please consider an object object and a decorator decorator. object has one method called methodA(). decorator, because we're in the Decorator pattern, also has this method by inheritance (both object and decorator eventually inherit from the same super-class which has methodA(). Let's call it AbstractObject).

methodA() in object looks like this:

public void methodA(){
    // do stuff
}

methodA() in decorator looks like this:

public void methodA(){
    // do some extra stuff.
    wrappedObject.methodA(); // and then delegate to the wrapped object.
}

It would be used like this:

AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object.methodA(); // does stuff and some more stuff. the extended methodA().

The point of decorator is to extend the functionality of object, and it does this by extending one of object's current methods. So far, classic Decorator.

Now, my question:

As I said Decorator usually extends an object's functionality by adding content to one of the object's current methods.

But what if I wanted to use Decorator to add public methods to an object?

Please consider the following scenario where decorator has another method methodB():

AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
((ConcreteDecorator)object).methodB();
// methodB() belongs to ConcreteDecorator() but not to AbstractObject().

As you see if I wanted to do this, I would have to cast object to ConcreteDecorator.

This exposes the concrete type of object and the fact that it's decorated, and as such somewhat defeats the intent of the Decorator pattern. Also, the code invoking methodB() would have to know object is decorated. Again, not very much in the spirit of Decorator.

In light of this I have two questions:

  • Is the Decorator pattern ever used this way? Is it ever used to add
    public methods to an object, as opposed to only 'extending' it's
    current methods?

  • Are there situations where this would be a good idea or a wise use of
    Decorator?

Best Answer

The decorator pattern is typically used to avoid an explosion of subclasses. A common example involves UI windows that you may want to have any combination of n attributes (scrollbar, titlebar, resizable, movable, etc.).
Supporting all combinations of all those N attributes for all UI windows M subclasses would involve making N * M subclasses.

The decorator pattern prevents the explosion by wrapping around the base window class' existing public methods.
The decorator pattern ideally would only require N + M classes in this case.

For the case you have described, you are adding new public methods; therefore, the decorator pattern probably is not ideal. I cannot think of any situations where it would be ideal to add new methods via a decorator.

A few alternatives to consider:

Subtyping

It is valid to use subtyping to add methods, provided you are keeping the SOLID principles in mind. Therefore, you might consider extending the concrete object to support methodB and then design the decorator around it.

AbstractObject object = new ExtendedConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
(ConcreteDecorator)object.methodB();
// methodB() belongs to ExtendedConcreteObject() but not to AbstractObject()

Adapter Pattern

Another way to view the problem: by adding a public method, you are changing the object's interface. This can be done by using the adapter pattern. To keep the concrete decorator simple, you might consider creating a decorator and then building an adapter for that decorator.

AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object = new ConcreteAdapter(object);
(ConcreteAdapter)object.methodB();
// methodB() belongs to ConcreteAdapter() but not to AbstractObject() or ConcreteDecorator().