Decorator pattern

Defition

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Introduction

While thinking about extending functionality, the first thing which is likely to occur to a new programmer is inheritance. However, inheritance may not be the ideal solution in all situations. When you inherit functionality through subclassing, the functionality is statically set at compile time and it doesn’t always lead to the most flexible nor maintainable designs. If you need to add new features, code modifications are required, which is a violation of the Open Closed Principle.

Instead, you can attach new responsibility to an object dynamically. This is exactly the intended use of the decorator pattern. Per the Gang of Four – “Attach additional responsibilities to an object dynamically”. But how do you do this? By using Composition. With composition, you can dynamically add multiple new responsibilities to objects at run time. The great thing here is that the object doesn’t need to be aware of it, and your design conforms to the Open Closed Principle. We often look to the elegance of a great object-oriented programming language such as Java, while overlooking the simplicity of using composition to solve a problem.

Participants

Consider a florist who sells flower bouquets, such as rose bouquet, orchid bouquet, and so on. When a customer walks in, the florist describes the bouquets and tells their prices. We can model the requirements with a FlowerBouquet abstract base class and subclasses for specific bouquets.

Customers, in addition to a bouquet, can ask the florist to decorate it with paper wrappings, ribbon bows, and glitters for which the florist charges extra. To address the new requirement, we can add new subclasses of FlowerBouquet, one each to represent a bouquet with an additional decoration, and this is how our design looks like now.

What we have is “class explosion” and that too while adding only a single decoration to a bouquet. Imagine representing a bouquet with multiple decorations, for example, a rose bouquet with both ribbon bow and glitter, or an orchid bouquet with a paper wrap, ribbon bow, and glitter. Some more considerations:

  1. What if we want a rose bouquet with double paper wrap?
  2. What if we want to add a new lily bouquet?
  3. What if we want to add new ornamental leaves decoration?

Clearly, our design is flawed, but this is an ideal use case for the decorator pattern. By using the decorator pattern, we can create a flower bouquet and dynamically decorate it with any numbers of features at run time. Without the flower bouquet knowing it that is “being decorated”. For that, we will create an abstract base class FlowerBouquet and specific subclasses RoseBouquet and OrchidBouquet to extend from it. We will also create an abstract FlowerBouquetDecorator class to extend FlowerBouquet. For each decorator, we will create the Glitter, PaperWrapper, and RibbonBow classes to extend from FlowerBouquetDecorator. This is how the design will be.

In the diagram above, the most important thing to observe is the relationship between FlowerBouquet and FlowerBouquetDecorator. We have two types of relationships:

  1. Inheritance by subclassing FlowerBouquetDecorator from FlowerBouquet in order to have the correct type.
  2. Composition by composing FlowerBouquetDecorator with FlowerBouquet in order to add new behavior.

Let’s now look at how our classes map to the participants of the decorator pattern:

  1. Component (FlowerBouquet): Is an abstract base class that can be decorated with responsibilities dynamically.
  2. ConcreteComponent(RoseBouquet and OrchidBouquet): Are concrete classes that extends Component to represent objects to which additional responsibilities can be attached.
  3. Decorator (FlowerBouquetDecorator): Is an abstract class that extends Component and acts as the base class for concrete decorator classes.
  4. ConcreteDecorator (PapperWrapper, RibbonBow, and Glitter): Are concrete classes that extends Decorator to decorate Components with responsibilities.

What we will learn?

Lets look at a few things

  1. The typical use of inheritance
  2. Learn how to decorate your classes at runtime using a form of object composition. Why? Once you know the techniques of decorating, you will be able to give your objects new responsibilities without making any code changes to the underlying classes.

Use case

(From the book Head First Design Patterns)

class design for a coffee shop

In addition to coffee, there can be several condiments like steamed milk, soy, mocha and whipped cream. So, how will we handle this?

As we can see, there is a class explosion.

Why do we need so many classes? Why don’t we use instance variables and inheritance as shown below?

Base class

public class Beverage {
    // instance varialbes for milkCost, soyCost, mochaCost and whipCost
    // getters and setters for milk, soy, mocha, whip
    public  double cost() {
        float condimentCost = 0.0F;
        if (hasMilk) {
            condimentCost += milkCost;
        }
        if (hasSoy) {
            condimentCost += soyCost;
        }
        if (hasMocha) {
            condimentCost += mochaCost;
        }
        if (hasWhip) {
            condimentCost += whipCost;
        }
        return condimentCost;
    }
}

Sub class

public class DarkRoast extends Beverage {
    public DarkRoast() {
        description = "Most excellent Dark Roast";
    }
    public  double cost() {
        return 1.99 + super.cost();
    }
}

What are the problems with this approach?

There are potential problems if you think about how the design might have to change in the future.

  1. Price changes for condiments wll force us to alter existing code
  2. New condiments will force us to add new methods and alter the cost method in the superclass
  3. We may have new beverages. For some of these beverages (iced tea), the condiments may not be appropriate. Yet, the tea subclass will still inherit methods like hasWhip()
  4. What if a customer wants a double mocha?

While inheritance is powerful, it doesn’t always lead to the most flexible or maintainable designs. How can we achieve reuse if not through inheritance? There are ways of “inheriting” behavior at runtime through composition and delegation. When you inherit behavior by subclassing, that behavior is set statically at compile time. In addition, all subclasses must inherit the same behavior. However, if you extend an object’s behavior through composition, then yu can do this dynamically at runtime. That is the power of composition. It is possible to add multiple new responsibilities to objects through this technique, including responsibilities that were not even thought of by the designer of the superclass. And, you will not have to touch their code. What is the effect of composition on maintaining your code? By dynamically composing objects, you can add new functionality by writing new code rather than altering existing code. Because you are not changing existing code, the chances of introducing bugs or causing unintended side effects in pre-existing code are much reduced.

The decorator pattern definition

The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

How will you apply it?

We start with a beverage and decorate it with the condiments at runtime. e.g. if a customer wants a Dark Roast with Mocha and Whip, we will

  1. Take a DarkRoast object
  2. Decorate it with a Mocha object
  3. Decorate it with a Whip object
  4. Call the cost() and rely on delegation to add on the condiment costs.

How this is done.

  1. Decorators have the same supertype as the objects they decorate.
  2. You can use one or more decorators to wrap an object.
  3. Given that the decorator has the same supertype as the object it decorates, we can pass around a decorated object in place of the original (wrapped) object.
  4. The decorator adds its own behavior either before and/or after delegating to the object it decorates to do the rest of the job.
  5. Objects can be decorated at any time, so we can decorate objects dynamically at runtime with as many decorators as we like.

Decorating our beverages

Some confusion over Inheritance vs Composition

Can we not use Inheritance and rely on Composition alone? Why is CondimentDecorator extending the Beverage class? It is vital that the decorators have the same type as the objects they are going to decorate. So, we use inheritance to achieve the type matching, but we are not using inheritance to get behavior.

Where does the new behavior come in? When we compose a decorator with a component, we are adding new behavior. We are acquiring new behavior not by inheriting it from a superclass, but by composing objects together. The behavior comes in through the composition of decorators with the base components as well as other decorators. And because we are using object composition, we get a whole lot mre flexibility about how to mix and match condiments and beverages. If we rely on inheritance, then our behavior can only be determined statically at complile time. In other words, we get only whatever behavior the superclass gives us or that we override. With composition, we can mix and match decorators any way we like… at runtime. And we can implement new decorators at any time to add new behavior. If we relied on inheritance, we would have to go in and change existing code any time we want new behavior.

Re-writing the coffee shop code to use Decorator pattern:

Beverage

public abstract class Beverage {
    String description = "Unknown beverage";
    public String getDescription() {
        return description;
    }

    public abstract double cost();
    public enum Size { SMALL, MEDIUM, LARGE };
    Size size = Size.LARGE;
    public void setSize(Size size) {
      this.size = size;
    }
    public Size getSize() {
      return this.size;
    }
}

CondimentDecorator

public abstract class CondimentDecorator extends Beverage {
  public Beverage beverage;
  public abstract String getDescription();
  public Size getSize() {
    return beverage.getSize();
  }
}

Espresso

public class Espresso extends Beverage {
  public Espresso() {
      description = "Espresso";
  }
  public double cost() {
    return 1.99;
  }
}

HouseBlend

public class HouseBlend extends Beverage {
  public HouseBlend() {
      description = "House Blend Coffee";
  }
  public double cost() {
    return 0.89;
  }
}

CondimentDecorator

public class Mocha extends CondimentDecorator {
  Beverage beverage;
  public Mocha(Beverage beverage) {
    this.beverage = beverage;
  }
  public String getDescription() {
    return beverage.getDescription() + ", Mocha";
  }
  public double cost() {
    return beverage.cost() + 0.20;
  }
}

CondimentDecorator

public class Soy extends CondimentDecorator {
  public Soy(Beverage beverage) {
    this.beverage = beverage;
  }
  public String getDescription() {
    return beverage.getDescription() + ", Soy";
  }
  public double cost() {
    double cost = beverage.cost();
    if (beverage.getSize() == Size.SMALL) {
      cost += .10;
    } else if (beverage.getSize() == Size.MEDIUM) {
      cost += .15;
    }
    if (beverage.getSize() == Size.LARGE) {
      cost += .20;
    }
    return cost;
  }
}

CoffeeShopTester

public class CoffeeShopTester {
  public static void main(String args[]) {
    Beverage beverage1 = new Espresso();
    Beverage beverage2 = new DarkRoast();
    beverage2 = new Mocha(beverage2);
    beverage2 = new Mocha(beverage2);
    beverage2 = new Whip(beverage2);
    Beverage beverage3 = new HouseBlend();
    beverage3 = new Soy(beverage3);
    beverage3 = new Mocha(beverage3);
    beverage3 = new Whip(beverage3);
  }
}

Real world decorators

The large number of classes in the java.io.package is overwhelming. The java.io package is largely based on Decorator Pattern. So the I/O classes should make more sense now that you know the Decorator Pattern.

Decorating the java.io classes.

  1. FileInputStream is the component that is being decorated. The Java I/O library supplies several components, including FileInputStream, StringBufferInputStream, ByteArrayInputStream, and a few others. All of these give us a base component from which to read bytes.
  2. BufferedInputStream is a concrete decorator. BufferedInputStream adds buffering behavior to a FileInputStream - it buffers input to improve performance.
  3. LineNumberInputStream is also a concrete decorator. It adds the ability to count the line numbers as it reads data.
  4. BufferedInputStream and LineNumberInputStream both extend FilterInputStream, which acts as the abstract decorator class.
  5. InputStream is the abstract component.
  6. FilterInputStream is an abstract decorator.
  7. FileInputStream, StringBufferInputStream and ByteArrayInputStream act as the concrete components that we will wrap with decorators. There are a few more that are not shown here - like ObjectInputStream.

We could now look over the java.io API docs and compose decorators on the various input streams. You will also see that the output streams have the same design. The Reader/Writer streams (for character-based data) closely mirror the design of the streams classes (with a few differences and inconsistencies, but close enough to figure out whats going on).

Java I/O also points out one of the downsides of the Decorator Pattern: designs using this pattern often result in a large number of small classes that can be overwhelming to a developer trying to use the Decorator-based API.

Writing your own Java I/O Decorator: Write a decorator that converts all uppercase characters to lowercase in the input stream.

import java.io.*;

public class LowerCaseInputStream extends FilterInputStream {

        public LowerCaseInputStream(InputStream in) {
                super(in);
        }

        public int read() throws IOException {
                int c = in.read();
                return (c == -1 ? c : Character.toLowerCase((char)c));
        }

        public int read(byte[] b, int offset, int len) throws IOException {
                int result = in.read(b, offset, len);
                for (int i = offset; i < offset+result; i++) {
                        b[i] = (byte)Character.toLowerCase((char)b[i]);
                }
                return result;
        }
}

Test code for this:

import java.io.*;

public class InputTest {
        public static void main(String[] args) throws IOException {
                int c;
                InputStream in = null;
                try {
                        in =
                                new LowerCaseInputStream(
                                        new BufferedInputStream(
                                                new FileInputStream("test.txt")));

                        while((c = in.read()) >= 0) {
                                System.out.print((char)c);
                        }
                } catch (IOException e) {
                        e.printStackTrace();
                } finally {
                        if (in != null) { in.close(); }
                }
                System.out.println();
                try (InputStream in2 =
                                new LowerCaseInputStream(
                                        new BufferedInputStream(
                                                new FileInputStream("test.txt"))))
                {
                        while((c = in2.read()) >= 0) {
                                System.out.print((char)c);
                        }
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }
}

Some downsides with Decorator pattern

It has the power to add flexibility to designs. But it can sometimes add a lot of small classes to a design and this occasionally results in a design that is less than straight-forward for others to understand. Take the Java I/O libraries. They are notoriously difficult for people to understand at first. But if they just saw the classes as a set of wrappers around an InputStream, life would be much easier.

People sometimes take a piece of client code that relies on specific types and introduce decorators without thinking through everything. One great thing about Decorator Pattern is, you can usually insert decorators transparently and the client never has to know it is dealing with a decorator. But some code is dependent on specific types and when you start introducing decorators, bad things happen. Introducing decorators can increase the complexity of the code needed to instantiate the component. Once you’ve got decorators, you’ve got to not only instantiate the component, but also wrap it with who knows how many decorators. The Factory and Builder patterns can be very helpful with this.

But Decorator Pattern is a great pattern for creating flexible designs and staying true to the Open-Closed Principle.

Conclusion

The decorator pattern is a great solution to dynamically extend behavior without subclassing, but it often results in a large number of decorator classes. While there are no complexities in creating the classes and applying the pattern, it can sometimes become overwhelming for new programmers to figure out and use the existing decorator-based code. The java.io API is largely based on the decorator pattern, and now that you have learned it, you will be at ease while wrapping input and output streams with decorator classes, such as BufferedInputStream and DataInputStream. Also, if you feel that clients might find your decorator-based code complex, you can always encapsulate the complexity by using the Factory Method, Abstract Factory, or Builder creational patterns.

Decorator pattern also comes in use during Enterprise Application Development with the Spring Framework. You will often need to perform tasks, such as transaction, cache synchronization, and security-related tasks. If you don’t want to risk cluttering up the business code, use decorators. Often, use of the decorator pattern will lead you to cleaner and more maintainable code.

Reading material

https://github.com/explorer436/programming-playground/tree/main/java-playground/design-pattern-samples/design-pattern-examples

  1. https://springframework.guru/gang-of-four-design-patterns/decorator-pattern/
  2. https://www.javadevjournal.com/java-design-patterns/decorator-design-pattern/

Tags

The Open-Closed design principle