Java设计模式:装饰者模式学习与分析

1 设计原则

  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程
  • 为交互对象之间的松耦合设计而努力
  • 对扩展开发,对修改关闭

2 装饰者模式

  • 动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
  • 装饰者和被装饰对象有相同的超类型
  • 可以用一个或多个装饰者包装一个对象
  • 用装饰过的对象替代原来的对象
  • 装饰者可以在所委托被装饰者的行为之前/后,加上自己的行为
  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用喜欢的装饰者来装饰对象
  • 装饰者模式比继承更具有弹性

3 举例

点餐台应用场景:

  • 咖啡做法有4种:DarkRoast(深度烘焙 )、Decat(德卡)、Espresso(浓咖啡)和HouseBlend(混合)四种,且以后可能会增加种类
  • 咖啡调料有2种:Mocha(加摩卡)、Whip(加奶泡),且以后可能会增加种类

现在,客户点了如下的咖啡:深度烘焙且加摩卡和奶泡的咖啡。
Java设计模式:装饰者模式学习与分析_第1张图片

代码要求:易扩展、松耦合

1、第一步:拿一个深焙(DarkRoast)对象;
2、第二步:以摩卡(Mocha)对象装饰它;
3、第三步:以奶泡(Whip)对象装饰它;
4、第四步:调用cost()方法,并依赖委托(delegate)将调料的价格加上去。

设计原则:对扩展开发,对修改关闭

类图:
Java设计模式:装饰者模式学习与分析_第2张图片

4 Java实现

4.1 创建Beverage基类和CondimentDecorator装饰器

第一步:创建Beverage抽象类

//Beverage是一个抽象类,有两个方法:getDescription() 和  cost()
public abstract class Beverage {
	 protected String description = "Unknown Beverage";
	
	//getDescription() 已经在这里实现了,但是cost()必须在子类中实现
	public String getDescription() {
		return description;
	}
	
	public abstract double cost();
}

第二步:创建CondimentDecorator装饰器,继承Beverage

//首先,必须让CondimentDecorator能够取代 Beverage,所以将 CondimentDecorator扩展 Beverage类
public abstract class CondimentDecorator extends Beverage{
	//所有的调料装饰者都必须重新实现getDescription()方法。稍后我们会解释为什么......
	public abstract String getDescription();
}

4.2 创建“做法”类

1.做法一:DarkRoast

public class DarkRoast extends Beverage{

	public DarkRoast() {
		description = "Dark Roast Coffee";
	}
	
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 1.13;
	}
}

2.做法二:Decat

public class Decat extends Beverage{

	public Decat() {
		description = "Decat Coffee";
	}
	
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 1.67;
	}

}

3.做法三:Espresso

//浓缩咖啡
//首先,让Espresso扩展自Beverage类,因为Beverage是一种饮料
public class Espresso extends Beverage {
	
	public Espresso() {
		//为了要设置饮料的描述,写了一个构造器。description 实例变量继承来自Beverage
		description = "Espresso";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		//计算Espresso的价钱
		return 1.99;
	}

}

4.做法四:HouseBlend

public class HouseBlend extends Beverage{

	public HouseBlend() {
		description = "House Blend Coffee";
	}
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		//HouseBlend的价钱
		return 0.89;
	}

}

4.3 创建调料

1.调料一:Mocha


//摩卡是一个装饰者,所以让它扩展自CondimentDecorator
//CondimentDecorator扩展至Beverage
public class Mocha extends CondimentDecorator{
	/*
	 * 要让Mocha能够引用一个Beverage,做法如下:
	 * 1.用一个实例变量记录饮料,也就是被装饰者。
	 * 2.想办法让被装饰者(饮料)被记录到实例变量中。这里的做法是:把饮料当作构造器的参数,再由构造器将此饮料记录在实例变量中。
	 */
	Beverage beverage;
	
	//将上一个beverage传进来,用新的取代旧的
	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}
	
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		// 这里希望叙述不只是描述饮料(如“DarkRoast”),而是完整地连饮料都描述出来(如“DarkRoast, Mocha”)。
		// 所以首先利用被委托的做法,得到一个叙述,然后再附加上本实例的叙述。
		return beverage.getDescription()+", Mocha";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		// 首先调用委托给被装饰对象,已计算价钱。然后再加上Mocha的价钱
		return 0.20 + beverage.cost();
	}

}

2.调料二:Whip

// Whip是一个装饰者,所以让它扩展自CondimentDecorator
// CondimentDecorator扩展至Beverage
public class Whip extends CondimentDecorator{
	/*
	 * 要让Whip能够引用一个Beverage,做法如下:
	 * 1.用一个实例变量记录饮料,也就是被装饰者。
	 * 2.想办法让被装饰者(饮料)被记录到实例变量中。这里的做法是:把饮料当作构造器的参数,再由构造器将此饮料记录在实例变量中。
	 */
	Beverage beverage;
	
	public Whip(Beverage beverage) {
		this.beverage = beverage;
	}
	
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		// 这里希望叙述不只是描述饮料(如“DarkRoast”),而是完整地连饮料都描述出来(如“DarkRoast, Whip”)。
		// 所以首先利用被委托的做法,得到一个叙述,然后再附加上本实例的叙述。
		return beverage.getDescription()+", Whip";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		// 首先调用委托给被装饰对象,已计算价钱。然后再加上Whip的价钱
		return 0.66 + beverage.cost();
	}

}

当然还可以创建很多哦。。。

4.4 举例测试


/*
 * 抽象组件:Beverage
 * 具体组件:HouseBlend
 * 抽象装饰:CondimentDecorator
 * */
public class StarbuzzCoffee {
	public static void main(String args[]) {
		
		//定一杯Espresso,不需要调料,现在打印出名字和价格
		Beverage beverage = new Espresso();
		System.out.println(beverage.getDescription()+" $" + beverage.cost());
		
		//定一杯DarkRoast,加Mocah,现在打印出名字和价格
		Beverage beverage2 = new DarkRoast();
		beverage2 = new Mocha(beverage2);
		System.out.println(beverage2.getDescription()+" $" + beverage2.cost());

		//定一杯HouseBlend, 加Mocah,现在打印出名字和价格
		Beverage beverage3 = new HouseBlend();
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.getDescription()+" $" + beverage3.cost());
	}
}

输出:

Espresso $1.99
Dark Roast Coffee, Mocha $1.3299999999999998
House Blend Coffee, Mocha, Whip $1.75

要是不想自己敲,代码下载
提取码:1wra

你可能感兴趣的:(设计模式)