装饰者模式(Decorate Pattern)

装饰者模式定义:装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案

著名的咖啡屋示例,咖啡屋订单系统要求能够计算出每种咖啡的价格(个人感觉3种设计的主要差别在于抽象方式不同):

总共有3种饮品(HouseBlend,Decaf,Espresso),2种配料(Mocha,Whip)

设计一:

(咖啡 = 饮品,咖啡+配料 = 饮品,在抽象的过程中并没有考虑咖啡和配料之间的关系)

类太多,太复杂,如果增加一种饮品,增加一种配料,或者某一种饮品、配料的价格发生改变,要找到所有牵扯到的部分一一修改

设计二:

 

(配料抽象为饮品的属性)

着实简洁了不少:

public abstract class Beverage {
	public String description;
	private boolean mocha;
	private boolean whip;
	
	public String getDescription(){
		return this.description;
	};
	
	public void addMocha(){
		this.mocha = true;
	}
	
	public void addWhip(){
		this.whip = true;
	}
	
	public double cost(){
		double price = 0d;
		if(mocha){
			price += 0.5;
		}
		if(whip){
			price += 0.1;
		}
		return price;
	}
}
public class Decaf extends Beverage {
	public Decaf() {
		this.description = "Decaf";
	}

	public double cost(){
		double price = super.cost();
		price += 2.5;
		return price;
	}
}
public class HomeBlend extends Beverage {
	public HomeBlend() {
		this.description = "HomeBlend";
	}
	
	public double cost(){
		double price = super.cost();
		price += 2.0;
		return price;
	}
}
public class Espresso extends Beverage {
	public Espresso() {
		this.description = "Espresso";
	}
	
	public double cost(){
		double price = super.cost();
		price += 2.2;
		return price;
	}
}

测试一下:

public class Test {
	public static void main(String[] args) {
		Beverage b = new HomeBlend();
		b.addMocha();
		b.addWhip();
		System.out.println(b.cost());
	}
}
2.6

考虑如下场景:

1,配料价格、种类的变化会迫使我们修改Beverage类,与此同时,当配料种类较多的时候,Beverage类将会变得相当庞大,难以维护

2,双倍mocha的Espresso怎么处理?

3,新饮品Icetea是不能加Mocha的,但是由于继承自Beverage类,依然获得了addMocha()方法,此时需要重写一个空方法,这种情况很常见,修改虽然不复杂但是显然设计二的可扩展性不高

设计三--装饰者模式:


装饰者模式分为3个部分:

1,抽象组件 -- 对应Beverage类

2,具体组件 -- 对应具体的饮品,如:HouseBlend,Decaf,Espresso

3,装饰着 -- 对应配料,如:Mocha,Whip

装饰者模式有3个特点:

1,具体组件和装饰者都继承自抽象组件(HouseBlend和Mocha都继承自Beverage)

2,可以使用装饰者组合具体组件创造出新的类(Mocha组合HouseBlend创造出“HouseBlend+Mocha”)

3,过程2可以重复,直到创造出需要的类

使用Decorate Pattern来获得一个Double Mocha Whip Espresso:

(咖啡= 饮品,配料 = 饮品,咖啡和配料本质上没有区别)


public abstract class Beverage {
	public String description;
	
	public String getDescription(){
		return this.description;
	};
	
	public abstract double cost();
}
public class Espresso extends Beverage {
	public Espresso() {
		this.description = "Espresso";
	}
	
	public double cost(){
		return 2.2;
	}
}
public abstract class BeverageDecorator extends Beverage{
	public abstract String getDescription();
}
public class Mocha extends BeverageDecorator {
	private Beverage b;
	
	public Mocha(Beverage b){
		this.b = b;
	}
	public String getDescription() {
		return b.getDescription()+",Mocha";
	}

	public double cost() {
		return b.cost() + 0.5;
	}
}

测试一下:

public class Test {
	public static void main(String[] args) {
		Beverage b = new Espresso();
		b = new Mocha(b);
		b = new Mocha(b);
		b = new Whip(b);
		System.out.println(b.getDescription());
		System.out.println(b.cost());
	}
}
Espresso,Mocha,Mocha,Whip
3.3000000000000003

当然decorator中可以重写父类的方法,也可以扩展自己需要的方法:

public class Mocha extends BeverageDecorator {
	private Beverage b;
	
	public Mocha(Beverage b){
		this.b = b;
	}
	public String getDescription() {
		return b.getDescription()+",Mocha";
	}

	public double cost() {
		return b.cost() + 0.5;
	}
	
	public String getFeeling(){
		return "tasty";
	}
}
public class Test {
	public static void main(String[] args) {
		Beverage b = new Espresso();
		b = new Mocha(b);
		Mocha m = new Mocha(b);
		System.out.println(m.getDescription());
		System.out.println(m.cost());
		System.out.println(m.getFeeling());
		Whip w = new Whip(m);
		System.out.println(w.getDescription());
		System.out.println(w.cost());
	}
}

想要使用扩展方法,则必须针对特定类,并且扩展方法的使用受到限制(Whip w = new Whip(m);之后,m扩展的getFeeling()方法将不再能使用),当然如果能确定装饰者是叶子节点的话,这也不算是个问题

Espresso,Mocha,Mocha
3.2
tasty
Espresso,Mocha,Mocha,Whip
3.3000000000000003

 

ps:

装饰者模式的缺点:

1,装饰者模式虽然扩展性较高,但是没有设计二简洁,类的数量略多(但肯定比设计一少很多),如何取舍扩展性和简洁是个问题,有所选择就要有所牺牲

2,很难搞清楚一个类究竟被装饰了多少层,可能是1层,也可能是100层

 

posted @ 2013-05-28 17:29  心意合一  阅读(261)  评论(0编辑  收藏  举报