Page 1 of 1

Design Patterns: Decorator Pattern Rate Topic: -----

#1 master.bennett  Icon User is offline

  • New D.I.C Head
  • member icon

Reputation: 16
  • View blog
  • Posts: 41
  • Joined: 09-September 11

Posted 26 September 2011 - 07:14 PM

Prereq, basic knowledge of:
- Inheritance
- Interfaces
- Composition

Design patterns - tried and tested methods for solving commonly occuring problems in software development

Decorator Pattern

The problems it tries to solve:
- to attach additional behaviour/responsibilities to an object dynamically

You may have some objects of a certain class you want to add additional behaviour to but not for all objects of the class. Instead of subclassing for each different combination, you will be able to create each component and attach them to an instance of the class. This will become much clearer as we get to the examples.


How this is achieved:
- using composition to wrap the class.

If you have not covered GUIs or File IOs, you may skip to the Example Problem Domain Section.

Common examples:
- File IO:
[code
FileReader fileReader = new FileReader("something.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
[/code]
Long story short, the FileReader object is wrapped with a BufferedReader to add extra behaviour for reading files

- Swing (e.g. TextArea). Here's a small snippet of how to add a scroll pane to text area in swing:
// the content to be displayed in the text area
String content = "DIC is a large community for programmers and designers" +
                 " that allow beginners and experts to share their knowledge.\n\n" +
                 " It is free and allows anyone from hobyist to professionals"+
                 " contribute.\n\n"+
                 " There are tutorials for almost every topic you need as well as"+
                 " technology specific forums";

// create the text area                 
JTextArea contentArea = new JTextArea(content);
contentArea.setEditable(false);
contentArea.setLineWrap(true);
contentArea.setWrapStyleWord(true);
        
// We create the ScrollPane and instantiate it with the TextArea as an argument
// along with two constants that define the behaviour of the scrollbars.
JScrollPane area = new JScrollPane(contentArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                                   JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);


As you can see, the text area is wrapped within a JScrollPane object.

Example Problem Domain.

We need a program to manage and calculate the price of beverages for a coffee shop. Each beverage that is bought can be either purchased individually by a customer or the customer may choose to have 1 or more additions such as milk, sugar, mocha, etc. along with the beverage.

In our example we are going to see how to use the Decorator pattern to create a small program to solve this problem.

The types of beverages we will have for example are: HouseBlend, Expresso
What we will be decorating these beverages with are: Milk, Mocha

One way this problem could be solved would be to create an Abstract class called Beverage and have each beverage along with 1 or more condiment as a subclass. You don't need to be a mathematician to work out that you will end up with (the number of beverages) * (the number of condiment) subclasses. E.g. Expresso, ExpressoWithMilk, ExpressoWithMocha, HouseBlendWithMilk, HouseBlendWithMocha, etc

Imagine if a new beverage was introduced. You would have to create a class for the beverage as well as the beverage combined with each condiment. This is time consuming. How about if someone wanted an Expresso with both Milk & Mocha or even 2 milks.

To solve this problem, first we start with an abstract Beverage class. This will contain all the attributes and behaviours all beverages and condiments should have. For this example we will keep it simple by having the description of the beverage as well as what we are interested in, the price.

package decoratortut;
public abstract class Beverage {
  // the description of the beverage
	private String description;
	
  // sets the description of the object to description
	protected Beverage(String description){
		this.description = description;
	}
  
  // returns the cost of the beverage. To be implemented in subclasses.
	public abstract double getCost();
	
  // returns the description of the object
	public String getDescription(){
		return this.description;
	}
}


Next we will create the beverages the coffee shop will be selling. This is a simple step. We simply create each unique beverage as a subclass extending the Beverage class. We will have a constant field that will hold the price of the Beverage. We will also override the getCost() method to return this value.

package decoratortut;
public class HouseBlend extends Beverage{
	// cost of a house blend
	private static final double HOUSEBLENDCOST = 1.30;

  // sets the description of the object to "House Blend"
	protected HouseBlend(){
		super("House Blend");
	}
	
	@Override
	public double getCost() {
		return HOUSEBLENDCOST;
	}
}


package decoratortut;
public class Expresso extends Beverage{
	// cost of an expresso
	private static final double EXPRESSOCOST = 1.50;
  
  // sets the description of the object to "Expresso"
	protected Expresso() {
		super("Expresso");
	}

	@Override
	public double getCost() {
		return EXPRESSOCOST;
	}
}


Now that we have our Beverages, we have got to think of how to combine these with a varying condiments. At compile time we may not know what a particular customer would like to order and yet we have to provide a way of handling any combination the customer orders.

It may seem very complicated but actually a very simple idea is used. Composition. This is the idea of objects that contain objects or more correctly, an object that has another object as one of its fields. With the Decorator design pattern, we treat decorators as a type of the original component. In other terms, each decorator is treated as a subclass of the abstract component. So milk for example is treated as a type of beverage.

For our next step we have an abstract class to represent Beverage decorators which will extend the Beverage class. This will allow us to add more decorators easily in future if we need to by simply subclassing. This is where we will wrap the beverages using composition in order to attach extra characteristics. This will be achieved by passing in the Beverage we want to decorate to the decorator.

First out BeverageDecorator class:

package decoratortut;
public abstract class BeverageDecorator extends Beverage {

	// the wrapped beverage
	protected Beverage beverage = null;
	
  // takes in a beverage and sets the description of the decorator
  // to the description of the beverage. We will see why this was done
  // in each specific decorator.
  // we set the wrapped beverage to the beverage we are trying to add
  // the decorator to
	protected BeverageDecorator(Beverage beverage) {
		super(beverage.getDescription());
		this.beverage = beverage;
	}

	@Override
	public abstract double getCost();
	
	@Override
	public abstract String getDescription();
}


In the next step we will create the 2 beverage decorators in our example, Milk & Mocha. This is done simply by extending the BeverageDecorator class. Each decorator will take in a beverage that it will be decorating as well as a constant value representing its cost. We will use the cost of the decorator and add it to the cost of the beverage to work out the total cost. We will also override the getDescription() method by combining the description of the original beverage and the description of the decorator comma seperator. We will see how these will be used to get the description of the customer's order when testing.

package decoratortut;
public class Mocha extends BeverageDecorator {
	// cost of mocha
	private static final double MOCHACOST = 0.50;
	
  // set the wrapped beverage
	public Mocha(Beverage beverage) {
		super(beverage);
	}
	
	@Override
	public double getCost() {
		// The existing beverage cost, plus the cost of a mocha
		return this.beverage.getCost() + MOCHACOST;
	}
	
	@Override
	public String getDescription() {
		// The existing description, plus Mocha.
		return this.beverage.getDescription() + ", Mocha";
	}
}


package decoratortut;
public class Milk extends BeverageDecorator {
	// cost of milk
	private static final double MILKCOST = 0.30;
	
  // set the wrapped beverage
	public Milk(Beverage beverage) {
		super(beverage);
	}
	@Override
	public double getCost() {
		// The existing beverage cost, plus the cost of milk
		return this.beverage.getCost() + MILKCOST;
	}
	@Override
	public String getDescription() {
		// The existing description, plus Milk.
		return this.beverage.getDescription() + ", Milk";
	}
}


Finally, we will use a main method to test the classes to make sure everything works as it should. This will show you how to decorate an existing beverage with a decorator, get its description and the total cost.

package decoratortut;
public class TestClass {

	public static void main(String[] args) {
		// a single express
		Expresso expresso = new Expresso();
		
		System.out.println("The cost of an expresso: ");
		System.out.println(expresso.getDescription());
		System.out.println(expresso.getCost());
		
		// add milk to the expresso by creating an instance of a Milk
    // and passing in the Expresso object
		Milk milk = new Milk(expresso);
		
		System.out.println("The cost of an expresso with milk: ");
		System.out.println(milk.getDescription());
		System.out.println(milk.getCost());
		
		System.out.println();
		System.out.println("-----------------------");
		
		// a single house blend
		HouseBlend houseBlend = new HouseBlend();
		System.out.println(houseBlend.getDescription());
		System.out.println(houseBlend.getCost());
		
		// add milk and mocha to the house blend
		Milk milk2 = new Milk(houseBlend);
		Mocha mocha = new Mocha(milk2);
		
		System.out.println(mocha.getDescription());
		System.out.println(mocha.getCost());	
	}
}


and here should be the results on the console:

The cost of an expresso: 
Expresso
1.5
The cost of an expresso with milk: 
Expresso, Milk
1.8

-----------------------
House Blend
1.3
House Blend, Milk, Mocha
2.1



This tutorial was meant to be as short and brief as possible to make it easy to understand and to form as a basis for further reading so I have some suggested reading below.

One way to get familiar may be to decide to extend this small program even further. You may decide to add extra Beverages and BeverageDecorators. You may even decide that the beverages in the example are too generic and extend the Beverage classes even further.

Further reading and examples:
- http://www.go4expert...=5127#decorator
- http://en.wikipedia....corator_pattern
- http://zishanbilal.w...orator-pattern/

Any suggestions and further explanation that you want, please let me know so I can update this.

Is This A Good Question/Topic? 4
  • +

Replies To: Design Patterns: Decorator Pattern

#2 Skiouros  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 31-October 11

Posted 31 October 2011 - 09:24 PM

Thanks for writing this nice tutorial, it really made things clear for me.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1