初学设计模式(5)-----装饰模式

回顾之前学到的工厂模式,装饰模式也属于一种用于创建特定对象的模式。区别在于,工厂模式,通过一个Factory对象来创建我们所需的对象,并且,讲创建过程封装进对应的Factory中,在外层无需知道对象到底是如何创建出来的;而对于装饰模式,我们完全可以在最外层控制对象的创建过程;从某种层面说,装饰模式就像是制作一杯清咖啡之后,通过一层一层的包装往里面添加其他佐料,而工厂模式则讲这一过程封装进了实际工厂类中。

这里先提出两个比较有用的词:Component和Decorator。

Component:如同下图的Espresso,是需要被装饰的对象,派生出多个Concrete component。

Decorator:如同下图的Mocha,Whip,装饰者类,用于装饰一个Component。

先来看一下装饰模式的抽象图表示:


这是一杯Double Mocha+Whip的Espresso(双倍摩卡+搅拌奶油+浓缩咖啡)。我的最爱啊!

好了,那要如何在java代码中实现这样一杯咖啡呢?

一般来说,我们一定会创建Coffee作为基类,然后将某个特定的类,比如这里的Double Mocha,去继承它。可是问题在于,假如客户不要double,是single呢?客户不需要浓缩咖啡,要美式咖啡呢?抑或是其他种类的咖啡呢?如果每一种都去继承基类,那日后的维护工作可就蛋疼了。

来看看装饰模式如何应对:

抽象基类,Beverage:

public abstract class Beverage {
	String description="no description";
	public String getDescription(){
		return this.description;
	}
	public abstract double cost();
}
浓缩咖啡类,Espresso:

public class Espresso extends Beverage {

	public Espresso(){
		this.description="Espresso";
	}
	
	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return 1.99;
	}

}

附加品基类,Condiment:

public abstract class CodimentDecorator extends Beverage {
	Beverage beverage;
	public abstract String getDescription();
}

Mocha:

public class Mocha extends CodimentDecorator {

	
	
	public Mocha(Beverage bev){
		this.beverage=bev;
	}
	
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription()+", Mocha";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return beverage.cost()+.20;
	}

}

Whip:

public class Whip extends CodimentDecorator {

//	Beverage beverage;
	
	public Whip(Beverage bev){
		this.beverage=bev;
	}
	
	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return beverage.getDescription()+", Whip";
	}

	@Override
	public double cost() {
		// TODO Auto-generated method stub
		return beverage.cost()+.10;
	}

}

Main函数:

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Beverage espresso =new Espresso();
		espresso=new Mocha(espresso);
		espresso=new Mocha(espresso);
		espresso=new Whip(espresso);
		System.out.println(espresso.getDescription()+"costs: "+espresso.cost());
	}
运行结果:

Espresso, Mocha, Mocha, Whipcosts: 2.49

这里关键在于,Beverage类是之后所有类的父类,是最上层的,通过多层继承,并且在Condiment类中hold住一个beverage的引用,来实现装饰模式,如此一来,便可将beverage和condiment分离,而今后即便是要添加一种新型饮料且不是咖啡品种,只要添加对应的beverage子类和对应的condiment类,即可实现,而不用修改现有的类。

在java中,其实已经存在了一种利用装饰模式实现的标准库,就是java io。一开始当我使用java io的时候,发现各种各样的input 和 output类,看得头昏眼花,不知道何时该用什么。其实,所有的输入输出类都继承自InputStream和OutputStream,并且在这基础上,对数据流做了不同的处理,才会出现DataInputStream,BufferedInputStream等等。如下图:


(PS:图是网上截的,貌似输出流和输入流这位作者都没写清楚。。。总之这里都是输入流,写错的地方请无视)

其中FilterInputStream就是一个装饰者的基类。从java doc中可以看到,它只是重写了InputStream的函数,但是并没有订制特定的函数,而这些特例都将在它的子类,也就是上面这幅图的最右边中实现。FilterInputStream只是一个提供给后面子类实现的Container。(PS:可以查看FilterInputStream的源代码)

所以我们经常会用到这样的代码:

InputStream ins=new ByteArrayInputStream(new BufferedInputStream(System.in));
对于很多初学者来说,看到这样的代码肯定头都晕了,我一开始也是,不过如果知道了装饰模式,这就很好理解了。其实ByteArrayInputStream就是一个Concrete Component,而之后的BufferedInputStream是一个Concrete Decorator。意思就是把控制台输入数据存入缓存,然后再变成字节数组输入流对象以便后续之用。

那么装饰者模式有什么坏处呢?其实很容易发现,component和decorator一多,势必是class explosion。后果。就不多说了。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值