Decorator Pattern

Problem

You want to add behavior or state to individual objects at run-time. Inheritance is not feasible because it is static and applies to an entire class.

What is the Decorator Pattern?

The Decorator Pattern attaches additional responsibilities to an object statically or dynamically. A Decorator provides an enhanced interface to the original object. In the implementation of this pattern, we prefer composition over an inheritance – so that we can reduce the overhead of subclassing again and again for each decorating element. The recursion involved with this design can be used to decorate our object as many times as we require.

The ornaments that are added to pine or fir trees are examples of Decorators. Lights, garland, candy canes, glass ornaments, etc., can be added to a tree to give it a festive look. The ornaments do not change the tree itself which is recognizable as a Christmas tree regardless of particular ornaments used. As an example of additional functionality, the addition of lights allows one to “light up” a Christmas tree.

Another example: an assault gun is a deadly weapon on its own. But you can apply certain “decorations” to make it more accurate, silent and devastating.

Decorator example

Purpose

  • Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
  • The Client-specified embellishment of a core object by recursively wrapping it.
  • Wrapping a gift, putting it in a box, and wrapping the box.

How to implement

  1. Make sure your business domain can be represented as a primary component with multiple optional layers over it.
  2. Figure out what methods are common to both the primary component and the optional layers. Create a component interface and declare those methods there.
  3. Create a concrete component class and define the base behavior in it.
  4. Create a base decorator class. It should have a field for storing a reference to a wrapped object. The field should be declared with the component interface type to allow linking to concrete components as well as decorators. The base decorator must delegate all work to the wrapped object.
  5. Make sure all classes implement the component interface.
  6. Create concrete decorators by extending them from the base decorator. A concrete decorator must execute its behavior before or after the call to the parent method (which always delegates to the wrapped object).
  7. The client code must be responsible for creating decorators and composing them in the way the client needs.

Example

public interface Weapon {
	boolean aimAndFire();
}
public class Gun implements Weapon {

	@Override
	public boolean aimAndFire() {
		System.out.println("fire gun!!!");
		return true;
	}

}

public class AK47 extends WeaponDecorator {

	public AK47(Weapon weapon) {
		super(weapon);
	}
	
	@Override
	public boolean aimAndFire() {
		// TODO Auto-generated method stub
		super.aimAndFire();
		
		// add additional functionalities.
		System.out.println("It's an AK47, fire 5 rounds a pull!");
		
		return true;
	}
}

public class Automatic extends WeaponDecorator {

	public Automatic(Weapon weapon) {
		super(weapon);
	}
	
	@Override
	public boolean aimAndFire() {
		super.aimAndFire();
		
		// add additional functionalities.
		
		System.out.println("It's an automatic, put on silencer and fire 10 rounds a pull!");
		
		return true;
	}
}

public class SniperRiffle extends WeaponDecorator {

	public SniperRiffle(Weapon weapon) {
		super(weapon);
	}
	
	@Override
	public boolean aimAndFire() {
		// TODO Auto-generated method stub
		super.aimAndFire();
		
		// add additional functionalities.
		System.out.println("It's a SniperRiffle, put on scope and fire 1 round a pull!");
		
		return true;
	}
}
public class DecoratorDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Weapon ak47 = new AK47(new Gun());

		System.out.println("fired ak47: " + ak47.aimAndFire());
		System.out.println();

		Weapon automatic = new Automatic(new Gun());

		System.out.println("fired automatic: " + automatic.aimAndFire());
		System.out.println();

		// here a riffle can fire like a riffle and also as a automatic(or other guns)
		Weapon riffle = new SniperRiffle(new Automatic(new Gun()));

		System.out.println("fired riffle: " + riffle.aimAndFire());
		System.out.println();

		List<Weapon> weapons = Arrays.asList(new AK47(new Gun()), new Automatic(new Gun()), new SniperRiffle(new Gun()));

		weapons.stream().forEach(weapon -> {
			System.out.println("fire weapon: " + weapon.aimAndFire());
			System.out.println();
		});
	}

}

Results

fire gun!!!
It's an AK47, fire 5 rounds a pull!
fired ak47: true

fire gun!!!
It's an automatic, put on silencer and fire 10 rounds a pull!
fired automatic: true

fire gun!!!
It's an automatic, put on silencer and fire 10 rounds a pull!
It's a SniperRiffle, put on scope and fire 1 round a pull!
fired riffle: true

fire gun!!!
It's an AK47, fire 5 rounds a pull!
fire weapon: true

fire gun!!!
It's an automatic, put on silencer and fire 10 rounds a pull!
fire weapon: true

fire gun!!!
It's a SniperRiffle, put on scope and fire 1 round a pull!
fire weapon: true

Uses

  • Use the Decorator pattern when you need to be able to assign extra behaviors to objects at runtime without breaking the code that uses these objects.
  • The Decorator lets you structure your business logic into layers, create a decorator for each layer and compose objects with various combinations of this logic at runtime. The client code can treat all these objects in the same way, since they all follow a common interface.
  • Use the pattern when it’s awkward or not possible to extend an object’s behavior using inheritance.
  • Many programming languages have the final keyword that can be used to prevent further extension of a class. For a final class, the only way to reuse the existing behavior would be to wrap the class with your own wrapper, using the Decorator pattern.

Advantages

  • The decorator pattern can be used to make it possible to extend (decorate) the functionality of a certain object at runtime.
  • The decorator pattern is an alternative to subclassing. Subclassing adds behavior at compile-time, and the change affects all instances of the original class; decorating can provide new behavior at runtime for individual objects.
  • Decorator offers a pay-as-you-go approach to adding responsibilities. Instead of trying to support all foreseeable features in a complex, customizable class, you can define a simple class and add functionality incrementally with Decorator objects.
  • You can extend an object’s behavior without making a new subclass.
  •  You can add or remove responsibilities from an object at runtime.
  •  You can combine several behaviors by wrapping an object into multiple decorators.
  •  Single Responsibility Principle. You can divide a monolithic class that implements many possible variants of behavior into several smaller classes.

Disadvantages

  • Decorators can complicate the process of instantiating the component because you not only have to instantiate the component, but wrap it in a number of decorators.
  • It can be complicated to have decorators keep track of other decorators because to look back into multiple layers of the decorator chain starts to push the decorator pattern beyond its true intent.
  •  It’s hard to remove a specific wrapper from the wrappers stack.
  • It’s hard to implement a decorator in such a way that its behavior doesn’t depend on the order in the decorator stack.
  •  The initial configuration code of layers might look pretty ugly.

Source code on Github




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published.