【设计模式】《Head First 设计模式》读书笔记——装饰者模式

装饰者模式:

动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。

涉及设计模式:

1.多用组合,少用继承
2.类应该对扩展开放,对修改关闭

问题简单描述:

星巴克有很多饮料。一开始的设计是有一个饮料超类(包含getDescription()、cost()方法),所有的饮料都继承这个饮料超类,但是饮料的种类非常的多,使得类的种类非常非常多,简直是“类爆炸”。后来又想到另外一种饮料基类,增加了包含了各种调料的布尔值,还有获取是否含有各种调料的方法,设置各种调料的方法,cost方法根据调料的布尔值判断是否加调料的价钱得出饮料的价格。只需要几种基本的饮料继承饮料基类,通过设置不同的调料就可以得到大部分的饮料,从而解决了类爆炸的问题。但是仍然存在很多问题,调料的价格调整或者新调料出现就需要改变基类的代码,也有一些饮料无法通过这样的方法表示(如双倍摩卡咖啡,因为一个调料在基类只有一个布尔值,虽然通过改变基类可以实现,但是基类变得复杂)。

解决思路:

用调料来“装饰”(包装可能更好理解)基本的饮料,每次计算价格的时候调用最外圈的cost(),外圈的cost()委托里层cost()来计算直到递归到最里层的基本饮料,再一层层的返回到最外层,没经过一层都会加上那一层的价格。

UML图:


实现代码:

Beverage基类:package decorator;

/**
 * 饮料超类(装饰者基类)
 * @author terry
 *
 */
public abstract class Beverage {

		String description="Unkonwn Beverage";

		public String getDescription() {
			return description;
		}
		
		public abstract double cost();

}

基本饮料

package decorator;

/**
 * 低咖啡因咖啡(基本饮料)
 * @author terry
 *
 */
public class Decaf extends Beverage{
	
	public Decaf(){
		description="Decaf";
	}

	@Override
	public double cost() {
		return 0.9;
	}
	

}
package decorator;

/**
 * 浓缩咖啡(基本饮料)
 *
 */
public class Espresso extends Beverage {
	
	public Espresso(){
		description="Espresso";
	}

	@Override
	public double cost() {
		return 1.99;
	}

}

调料超类(也需要继承饮料超类)

package decorator;

/**
 * 调料装饰者超类(也需要继承饮料超类,达到跟基本饮料属于同一个类型)
 * @author terry
 *
 */
public abstract class CondimentDecorator extends Beverage {
	
	public abstract String getDescription();

}

调料实现类

package decorator;

/**
 * 摩卡(调料实现类)
 * @author terry
 *
 */
public class Mocha extends CondimentDecorator{
	//调料实现类需要有饮料属性,需要有被装饰者
	Beverage beverage;
	
	public Mocha(Beverage beverage){
		this.beverage=beverage;
	}

	@Override
	public String getDescription() {
		return beverage.getDescription()+",Mocha";
	}

	@Override
	public double cost() {
		//计算价格先把调用委托给装饰对象,计算出价格后再加上摩卡本身的价格
		return beverage.cost()+0.3;
	}

}
package decorator;

/**
 * 奶泡(调料实现类)
 * @author terry
 *
 */
public class Whip extends CondimentDecorator {
	Beverage beverage;
	
	public Whip(Beverage beverage){
		this.beverage=beverage;
	}

	@Override
	public String getDescription() {
		return beverage.getDescription()+",Whip";
	}

	@Override
	public double cost() {
		return beverage.cost()+0.2;
	}

}

测试类

package decorator;

public class Test {
	
	public static void main(String[] args){
		//双摩卡奶泡浓缩咖啡
		Beverage beverage=new Espresso();
		beverage=new Mocha(beverage);
		beverage=new Whip(beverage);
		beverage=new Mocha(beverage);
		/*
		 * 可能这样写更有包裹的感觉
		 * beverage=new Mocha(new Whip(new Mocha(beverage)));
		 */
		System.out.println(beverage.getDescription()+" cost:"+beverage.cost());
	}

}

输出结果





个人觉得装饰者模式有一点突破常规想法的就是把装饰者(调料)跟被装饰者(饮料)看成了同一种类型,也只有当两者是同一种类型的时候才可以嵌套“装饰”。装饰者继承被装饰者基类是为了有正确的类型,而不是为了继承它的行为。装饰者模式达到了“运行时”动态的添加属性的神奇效果,但是装饰者模式往往让很多不了解它的实现原理的人觉得很可怕,我第一次见到java.io类的时候就觉得这样的代码很奇怪很难看 → →。知道这就是装饰者模式的一种表现后就理解其实所有的类都是为了装饰inputStream/outPutSteam/Reader/Writer,没装饰一种io类就是多了一种解析的方式,我这样想就觉得io类没那么可怕。

欢迎批评指正^ ^


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值