设计模式之装饰器模式

        生活中门是大家每天都可以见到的东西,我们以木门为例,一个木门主要是由木头组成的,如果有一天为了美观我们想在木门上面添加一些木材饰品,那么我们总不能直接换一个门吧,那多浪费啊。那么我们可以先选定一些木材饰品,然后装饰上去即可,这样门通过木材饰品的修饰更加美观了,而且也不会浪费资源。因此,这个装饰品起到了完善的功能,而且门对于装饰品有选择性,比如主人喜欢哪种饰品,那么门就装饰什么饰品。其实装饰器类就如同之前门装饰一般。门和装饰品都是属于木材,因此普通类与饰品类是统一接口的,这样在装饰的时候比较方便,比如门上安上一个铁饰品就明显显得比较费力。装饰器模式就是动态地给一个对象添加一些额外的职责,就扩展功能来说,装饰者提供了比继承更加有弹性的替代方案。

1.适用性和优缺点
1.适用性
a.在不影响其它对象的情况下,以动态地、透明的方式给单个对象添加职责。
b.处理那些可以撤销的职责
c.当不能采用子类的方法进行扩充时。

2.优点
a.装饰器模式可以提供比继承更多的灵活性。
b.可以通过一种动态方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同行为。
c.通过类的组合,形成不同行为的组合,功能更强大。
d.具体构建类与装饰器独立变化,使用时进行组合,原有代码不变,符合开闭原则。

3.缺点
a.因为装饰需要使用组合对象,所以会产生很多小对象,增加系统复杂性。
b.多次装饰对象,系统比较复杂,如果出现bug,需要逐级排查。

2.示例讲解
UML图:

Component:组件对象的接口,可以给这些对象动态的添加职责;

ConcreteComponent:具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;

Decorator:所有装饰器的父类,需要定义一个与组件接口一致的接口(主要是为了实现装饰器功能的复用,即具体的装饰器A可以装饰另外一个具体的装饰器B,因为装饰器类也是一个Component),并持有一个Component对象,该对象其实就是被装饰的对象。如果不继承组件接口类,则只能为某个组件添加单一的功能,即装饰器对象不能在装饰其他的装饰器对象。ConcreteDecorator:具体的装饰器类,实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。


        接下来我们还时回到文章开始的门的例子吧,进行讲解,首先定义一个组件接口,这里的组件接口是Wood接口,我们规定所有组件以及装饰品都是木制的,便于装饰品类的调用及对象的复用,其中operation()是一个基本功能。
package decoratorpattern;

public interface Wood {
	void operation();

}
接下来就是 Decorator,定义一个抽象类实现Wood接口,便于统一装饰品的接口,其实装饰品抽象类相当于小一级的组件接口。里面会传入组件接口对象,便于基本功能的调用。
package decoratorpattern;

public abstract class DecoratorWood implements Wood {
	protected Wood wood;
	public void setWood(Wood wood) {
		this.wood=wood;
	}

	@Override
	public void operation() {
		// TODO Auto-generated method stub
		wood.operation();

	}

}
然后是具体组件的实现,这里的具体组件指的是门,门实现了组件接口的operation()方法。
package decoratorpattern;

public class WoodDoor implements Wood {
	
	@Override
	public void operation() {
		// TODO Auto-generated method stub
		System.out.println("我是一个木门,静静地站在这里看着你");

	}

}
接着是具体装饰的实现,可以看出装饰品类继承装饰抽象类,并且在组件接口的基本功能上,增添了新的功能,这也是装饰器的作用,动态地给对象添加职责。

package decoratorpattern;

public class DecoratorA extends DecoratorWood {
	public void operation()
	{
		super.operation();
		plusOperation();
		
	}
	
	public void plusOperation()
	{
		System.out.println("我装饰了DecoratorA之后,可以旋转");
	}

}
package decoratorpattern;

public class DecoratorB extends DecoratorWood {
	public void operation()
	{
		super.operation();
		plusOperation();
		
	}
	public void plusOperation()
	{
		System.out.println("有了DecoratorB之后,我可以跳跃!");
	}

}
最后是客户端的类
package decoratorpattern;

public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Wood wood=new WoodDoor();
		DecoratorA d1=new DecoratorA();
		DecoratorB d2=new DecoratorB();
		
		d1.setWood(wood);
		d2.setWood(d1);
		d2.operation();

	}

}
  运行客户端程序,得到如下结果:

我是一个木门,静静地站在这里看着你
我装饰了DecoratorA之后,可以旋转
有了DecoratorB之后,我可以跳跃!
         根据结果,可以看出,门组件在自己的功能基础上,增加了A和B装饰器的功能,而且是动态添加的,这也是为什么装饰器抽象类实现组件接口,因为统一了接口,便于对象的操作。因此装饰器抽象类的setter函数很重要,便于将对象传入,其实这里用构造函数也是同样的道理,就是将组件传入装饰器类中进行包装,得到新组件,仍然可以将新组件传入另一个装饰器,完成新的装饰。
        其实在IO流这一块,我们有用到装饰器模式,我们可以看如下几行代码:
InputStream in=new FileInputStreanm("xxx.txt");
in=new BufferedInputStream(in);
in=new LowerCaseInputStream(in);
        上面那段代码就是使用了装饰器,首先设置好组件FileInputStream,读取相应文件,这是基本功能,然后用装饰器BufferedInputStream装饰它,并需要将FileInputStream对象传入BufferedInputStream中,添加了新功能之后,想对文件进行内容进行过滤,这时可以通过过滤器进行装饰,即用LowerCaseInputStream()装饰。这也是装饰器模式的具体实现。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值