Thursday, March 11, 2010

Decorator design pattern

When a computer manufacturer like Dell is confronted with a scenario of supplying basic computer with various add-ons, how does it provide the correct description for the configuration and its correct price? One option would be to create a Computer class and then subclass it with each variant like ComputerPlusWindows, ComputerPlusWindowsPlusExtraMemory etc. We can already see the classes proliferating and the nightmare gets worse when the price of a component changes as it would need to be reflected in each configuration using the changed price. Here the utility of Decorator design pattern becomes evident as we can can create wrapper classes for the variants which HAS-A computer. The trick is to create an abstract decorator for our original Computer class and let all the variant classes extend this decorator while wrapping themselves around the Computer class. In our example below, we have taken a basic Computer with price and description and created just two variants of Windows7Computer and ExtraMemoryComputer to correctly reflect the name of the add-on and the price with the add-on.

package raj;

/**
* @author Rajeev
*/
public class DecoratorTest {
public static void main(String[] arg) {
Computer computer = new Computer();
// put windows 7 wrapper
computer = new Windows7Computer(computer);
System.out.println(computer.getDescription());
System.out.println("Cost "+ computer.getPrice());
/* Basic computer + Winodws 7
Cost 230.75
*/
// put extra memory wrapper
computer = new ExtraMemoryComputer(computer);
System.out.println(computer.getDescription());
System.out.println("Cost "+ computer.getPrice());
/* Basic computer + Winodws 7 + 2G memory
Cost 281.5
*/
// put more extra memory
computer = new ExtraMemoryComputer(computer);
System.out.println(computer.getDescription());
System.out.println("Cost "+ computer.getPrice());
/* Basic computer + Winodws 7 + 2G memory + 2G memory
Cost 332.25
*/
}
}

class Computer {

private String description = "Basic computer";
private double price = 200.50;

public String getDescription() {
return description;
}

public double getPrice() {
return price;
}
}

abstract class ComputerDecorator extends Computer {

public abstract String getDescription();

public abstract double getPrice();
}

class Windows7Computer extends ComputerDecorator {

Computer computer;
private double price = 30.25;
private String description = "Winodws 7";

public Windows7Computer(Computer computer) {
this.computer = computer;
}

public String getDescription() {
return computer.getDescription() + " + " + description;
}

public double getPrice() {
return computer.getPrice() + price;
}
}

class ExtraMemoryComputer extends ComputerDecorator {

Computer computer;
private double price = 50.75;
private String description = "2G memory";

public ExtraMemoryComputer(Computer computer) {
this.computer = computer;
}

public String getDescription() {
return computer.getDescription() + " + " + description;
}


public double getPrice() {
return computer.getPrice() + price;
}
}

Now we can see that we can create variants of adding anti-virus, extra CD, network card etc and the wrapping works perfectly. In our design, it would have been better to put the reference to Computer in the ComputerDecorator then the concrete subclasses of Decorator but the net effect is the same. Incidentally the classes in java.io package pertaining to various streams and readers/writers use this pattern extensively. Whenever we need to add-on extra behaviour dynamically then Decorator is the answer.

No comments:

Post a Comment