装饰者模式

前言:我曾经以为男子汉应该用继承去处理一切,后来我领教到运行时扩展,远比编译时期的继承威力大的多。

装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。


以下情况会使用到Decorator模式
1. 需要扩展一个类的功能,或给一个类添加附加职责。
2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
下面进入实战,现在需求是一家果汁店有各种各样的果汁,eg:橙汁、柠檬汁... 还有各种附加调料,eg:加冰、加糖、加热(不同的调料有不同的费用),然后根据相应的果汁和调料计算总价格。
我们可能会更先想到 让我们的果汁 去继承一个父类Beverage,把各种调料当作成员变量,需要什么调料,把boolean置为true就好了,然后再cost()方法中去计算价格。 很显然这样做会产生“类爆炸”,并且,也为自己制造了维护噩梦,如果果汁的价格和调料的价格上涨了怎么办?
想到我之前所说的设计原则,对扩展开放,对修改关闭。
我们把饮料父类提取出来 
public abstract class Beverage {
	public String description = "Unknown Beverage";
	
	public String getDescription(){
		return description;
	}
	
	public abstract double cost();
	
}
装饰者超类也提取出来,被装饰者和装饰者拥有共同的父类
public abstract class CondimentDecorator extends Beverage{

	public abstract String getDescription();//所有的调料装饰者都必须重新实现getDescription()方法。
	
}
我们把饮料和调料分离
下面创建两种饮料 橙汁 和柠檬汁(被装饰者)
public class Orange extends Beverage {
	
	public Orange(){  //为了设置饮料的描述,我们写了一个构造,description继承自Beverage
		this.description = "橙汁"; 
	}
	
	public double cost() {
		return 2.00;
	}
}

public class Lemon extends Beverage{
	
	public Lemon(){
		this.description = "柠檬汁"; 
	}

	public double cost() {
		return 3.00;
	}

}

创建2种调料 加冰、加糖(装饰者)
/**
 * @author IT小蔡
 *  冰是一个装饰者,所以让它扩展自CondimentDecorator
 */
public class Ice extends CondimentDecorator {

	/**
	 * 用一个实例变量记录饮料,也就是被装饰者。
	 */
	Beverage beverage;

	/**
	 * @param beverage
	 *            想办法让被装饰者(饮料)被记录到实例变量中。这里的做法是:把饮料当作构造器的参数,再由构造器将此饮料记录在实例变量中。
	 */
	public Ice(Beverage beverage) {
		this.beverage = beverage;
	}

	/*
	 * (non-Javadoc) 叙述的不光是饮料(如橙汁),而是完整的把调料也描述出来
	 */
	public String getDescription() {
		return beverage.getDescription() + " ,加冰";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cai.yu.abstrac.Beverage#cost() 计算出加冰的饮料的价钱。
	 */
	public double cost() {
		double cost = beverage.cost() + 0.20;
		return cost;
	}

}



public class Suger extends CondimentDecorator{
	Beverage beverage;
	
	public Suger(Beverage beverage){
		this.beverage = beverage;
	}

	public String getDescription() {
		return beverage.getDescription()+" ,加糖";
	}

	public double cost() {
			double cost = beverage.cost() + 0.30;
			return cost;
		}
	
}

Test类
public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Beverage beverage1 = new Lemon(); 
		System.out.println(beverage1.getDescription()+" $"+beverage1.cost());//先订一杯柠檬汁,不加调料,打印出描述与价格
		
		Beverage beverage2 = new Orange(); //构造出一杯橙汁,什么都不加
		beverage2 = new Ice(beverage2); //用冰去装饰
		beverage2 = new Suger(beverage2); // 用糖去装饰
		System.out.println(beverage2.getDescription()+" $"+beverage2.cost());//订一杯加冰加糖的橙汁,打印出描述与价格
		
	}

}

运行结果






如果现在又有了新需求,要把每一种饮料分为 大、中、小 杯。分别加 0.2, 0.15, 0.10元。又该怎么实现?
为了尽量不去修改源代码,我想到的是 把杯子大小也当作一种装饰者,把杯子size,作为 Beverage的成员变量。
public abstract class Beverage {
	public String description = "Unknown Beverage";
	public static final String TALL = "小杯";
	public static final String GRANDE = "中杯";
	public static final String VENTI = "大杯";
	public String size; //默认是小杯的。
	
	public String getDescription(){
		return description;
	}
	
	public abstract double cost();
	
	public String getSize(){
		return size;
	}
	
	public void setSize(String size){
		this.size = size;
	}
}

杯子尺寸装饰者 Capacity
/**
 * @author IT小蔡
 * 把杯子也当成一种装饰者
 */
public class Capacity extends CondimentDecorator{

	Beverage beverage;
	
	/**
	 * @param beverage 被装饰的饮料
	 * @param size 杯子尺寸
	 */
	public Capacity(Beverage beverage,String size){
		this.beverage = beverage;
		this.size = size;
	}
	
	@Override
	public String getDescription() {
		return beverage.getDescription()+","+size;
	}

	@Override
	public double cost() {
		double cost = beverage.cost();
		switch (size) {
		case Beverage.TALL:
			cost+=0.10;
			break;
		case Beverage.GRANDE:
			cost+=0.15;
			break;
		case Beverage.VENTI:
			cost+=0.20;
			break;

		default:
			break;
		}
		return cost;
	}

Test类
public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Beverage beverage1 = new Lemon(); 
		System.out.println(beverage1.getDescription()+" $"+beverage1.cost());//先订一杯柠檬汁,不加调料,打印出描述与价格
		
		Beverage beverage2 = new Orange(); //构造出一杯橙汁,什么都不加
		beverage2 = new Ice(beverage2); //用冰去装饰
		beverage2 = new Suger(beverage2); // 用糖去装饰
		System.out.println(beverage2.getDescription()+" $"+beverage2.cost());//订一杯加冰加糖的橙汁,打印出描述与价格
		
		beverage2 = new Capacity(beverage2,Beverage.VENTI); //来一杯大杯加冰加糖的橙汁。
		System.out.println(beverage2.getDescription()+" $"+beverage2.cost());//先订一杯柠檬汁,不加调料,打印出描述与价格
		

	}

}
运行结果


java里面典型的装饰者应用,在 IO包里面,有兴趣的同学可以看看,
继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式,在我们的设计中,应该允许行为被扩展,而无需修改现有的代码,装饰者模式意味着一群装饰类,这些装饰类用来包装具体组件。你可以用无数个装饰者去包装一个组件,但装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂。




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值