一、定义
动态添加对象功能。通过委托机制,复用系统中的各个组件,在运行时,可以讲这些功能组件进行叠加,从而组成一个“超级对象”。
二、设计思想
代码复用应该尽可能使用组合(委托),而不是使用继承。因为继承是一种紧密耦合,任何父类的改动都会影响到子类,不利于系统维护。装饰者模式可以有效的分离性能组件和功能组件,从而有效提升模块的可维护性和复用性。
三、四个角色
(1)组件接口
组件接口是装饰者和被装饰者的超类或者公共接口。它定义了被装饰者的核心功能和装饰者需要加强的功能点。
(2)具体组件
具体组件实现了组件接口的核心方法,完成一个核心的功能。而其他性能方面或者额外的功能方面,交由装饰者去实现。
(3)装饰者
实现组件接口的一个类,并且持有一个组件(即被装饰者)对象。
(4)具体装饰者
实现了抽象的装饰者,完成具体的装饰逻辑,实现性能的增强。各个具体装饰者可以叠加,从而构成一个功能更强大的组件对象。
四、代码实现
IBuilding.java 具体组件和装饰者的公共接口,从而保证被装饰者和装饰者具有相同的接口
public interface IBuilding {
public String getDetails();
}
Building.java
public class Building implements IBuilding{
public String getDetails() {
// TODO Auto-generated method stub
return "一间普通的房子";
}
}
BuildingDecorator.java 装饰者的抽象类,持有一个被装饰对象
public abstract class BuildingDecorator implements IBuilding{
IBuilding building;
public BuildingDecorator(IBuilding building) {
this.building=building;
}
}
LightingDecorator.java 灯饰装饰
public class LightingDecorator extends BuildingDecorator {
public LightingDecorator(IBuilding building) {
super(building);
}
public String getDetails() {
// TODO Auto-generated method stub
StringBuffer sb=new StringBuffer();
sb.append(building.getDetails());
sb.append(",装上灯饰");
return sb.toString();
}
}
FlowerDecorator.java 鲜花装饰
public class FlowerDecorator extends BuildingDecorator {
public FlowerDecorator(IBuilding building) {
super(building);
}
public String getDetails() {
// TODO Auto-generated method stub
StringBuffer sb=new StringBuffer();
sb.append(building.getDetails());
sb.append(",放上几盆花");
return sb.toString();
}
}
LearnDecorator.java
public class LearnDecorator {
public static void main(String[] args) {
//通过层层构造,将装饰者添加到被装饰对象上,可以有选择性的添加装饰者
IBuilding building=new LightingDecorator(new FlowerDecorator(new Building()));
System.out.println(building.getDetails());
}
}
运行结果:
一间普通的房子,放上几盆花,装上灯饰
五、装饰者模式的具体应用场景
在JDK的实现中,有不少组件使用了装饰者模式。最典型的一个例子就是I/O。下面以OutputStream为例。
(1)组件接口:OutputStream
(2)具体组件:FileOutputStream
(3)装饰者:FilterOutputStream
(4)第一个具体的装饰者:BufferedOutputStream 可以对FileOutputStream增加缓冲功能,优化IO性能。
(5)第二个具体的装饰者:DataOutputStream 增加了对多种类型数据的写操作支持。
DataOutputStream outputStream=new DataOutputStream(new BufferedOutputStream(new FileOutputStream("f:/example.txt")));
outputStream.writeChars("hello world");