设计模式学习之装饰者模式--4

定义

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

案例

现在一个咖啡厅要升级订单系统,当购买咖啡时,可以加入相应的调料。例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha)或奶泡。并自动计算咖啡+调料的价格。
要知道咖啡也有很多种,比如有比如有以上种类的咖啡和配料以上种类的咖啡和配料以及价格。
那么我们可以考虑用装饰者模式了。

首先定义一个饮料的超类

public abstract class Beverage {
	public String description = "Unknown Beverage";
	//饮料的说明
	public String getDescription() {
		return description;
	}
	//饮料的价格
	public abstract double cost();
}

另外还会有综合、深培、低咖啡因、浓缩等具体的咖啡类

/**
 * 具体的咖啡
 * 深培咖啡
 *
 */
public class DarkRoast extends Beverage {
	public DarkRoast() {
		//给饮料说明赋值
		description = "DarkRoast";
	}
	//计算饮料的价钱
	public double cost() {
		return 1.99;
	}
}


/**
 * 具体的咖啡
 * 低咖啡因咖啡
 * @author user1
 *
 */
public class Decaf extends Beverage {
	public Decaf() {
		//给饮料说明赋值
		description = "Decaf";
	}
	//计算饮料的价钱
	public double cost() {
		return 1.05;
	}
}

Beverage很简单。让我们也来实现Condiment(调料)抽象类,也就是装饰者类吧:

/**
为何装饰者需要和被装饰者(亦即被包装的组件)有相同的“接口”,因为装饰者必须能取代被装饰者。
继承Beverage抽象类,是为了有正确的类型,而不是继承它的行为。行为来自装
饰者和基础组件,或与其他装饰者之间的组合关系。
*/
public abstract class CondimentDecorator extends Beverage {
	public abstract String getDescription();
}

接下来实现具体的装饰者

/**
 * 调料  具体的装饰者
 * 摩卡
 * @author user1
 *
 */
public class Mocha extends CondimentDecorator {
	Beverage beverage;

	/*
	 * 要让Mocha能够引用一个Beverage,做
		法如下:
		(1)用一个实例变量记录饮料,也就
		是被装饰者。
		(2)想办法让被装饰者(饮料)被记
		录到实例变量中。这里的做法是:把
		饮料当作构造器的参数,再由构造器
		将此饮料记录在实例变量中。
	 */
	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}
	/*
	 * 我们希望叙述不只是描述饮料(例
		如“Mocha”),而是完整地连调料都
		描述出来(例如“DarkRoast, Mocha”)。
		所以首先利用委托的做法,得到一个
		叙述,然后在其后加上附加的叙述(例
		如“Mocha”)
	 * */
	public String getDescription() {
		return beverage.getDescription() + ", Mocha";
	}
	public double cost() {
		return .20 + beverage.cost();
	}
}

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

接下来测试一下下订单,看能不能算出咖啡+调料的总价格

public class StarbuzzCoffee {
	public static void main(String args[]) {
		//定一杯深培咖啡,不加调料,显示描述与价格
		Beverage beverage = new DarkRoast();
		System.out.println(beverage.getDescription() + " $" + beverage.cost());
		// 定一杯低咖啡因咖啡,加摩卡,加奶泡,显示描述与价格
		Beverage beverage2 = new Decaf();
		beverage2 = new Mocha(beverage2);
		System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
		//定一杯低咖啡因咖啡,加摩卡,加奶泡,显示描述与价格
		Beverage beverage3 =  new Whip(new Mocha(new HouseBlend()));  //这种写法是不是似曾相识,是的,当我们包装输入输出流时,通常是这样的。
		System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
	}
}

真实世界的装饰者

Java Io包内许多类都是装饰者
在这里插入图片描述

BufferedInputStream及LineNumberInputStream都扩展自
FilterInputStream,而FilterInputStream是一个抽象的装饰类。
在这里插入图片描述
你会发现和我们的咖啡例子并没有多大的差异。

猜你喜欢

转载自blog.csdn.net/administratorJWT/article/details/85276409
今日推荐