装饰器模式:
动态地给一个对象添加一些额外的职责,装饰模式相比生成子类更为灵活。
Component:抽象构件
ConcreteComponent 具体构件,要被装饰的类
Decorator:装饰角色,具体装饰器的父类
ConcreteDecorator: 具体装饰器角色
示例:
对一个字符串打印前,进行两个处理:1、转大写,2、删除所有的 aa 字符串。这两步顺序要求不一样就会产生不一样的结果。
伪代码如下:
//定义一个打印的接口,及抽象构件
interface Component{
void print();//打印
}
//定义一个抽象类,实现需要处理的步骤,具体的打印可以在子类中实现。
abstract Test implements Component{
public void upcase(){}//转大写实现
public void replace(){}//替换实现
}
这样会发现每一个顺序都会对应一个子类的实现。如果再加上几种操作要求顺序不同,那么子类就会很多。这时候装饰器模式就很容易处理。动态扩展类的功能
装饰器模式实现如下:
1、还是使用上面的抽象构件
/**
* 抽象构件
*/
public interface Component extends Serializable{
void print(String str);
}
2、抽象构件要有一个实现类,即具体的构件
/**
* 具体构件
* 要被装饰的类
*/
public class ConcreteComponent implements Component{
@Override
public void print(String str) {
System.out.println(str);
}
}
3、每一个操作就是一个装饰器(ConcreteDecorator),装饰器要有一个装饰角色(Decorator),
装饰角色要实现抽象构件,并且要有抽象构件作为一个属性。
/**
* 装饰器父类
*/
public abstract class Decorator implements Component{
private Component component;
public Decorator(Component component){
this.component = component;
}
@Override
public void print(String str) {
component.print(str);
}
}
4、实现两个 ConcreteDecorator,都是Decorator 子类
/**
* 装饰器A
*/
public class DecoratorA extends Decorator{
public DecoratorA(Component component){
super(component);
}
@Override
public void print(String str) {
//转大写
str = str.toUpperCase();
super.print(str);
}
}
/**
* 装饰器A
*/
public class DecoratorB extends Decorator{
public DecoratorB(Component component){
super(component);
}
@Override
public void print(String str) {
//删除aa
str = str.replaceAll("aa","");
super.print(str);
}
}
5、测试
public static void main(String[] args) {
//构件
Component component = new ConcreteComponent();
//装饰器
component = new DecoratorB(component);
//装饰器
component = new DecoratorA(component);
component.print("aabcccazz");
}
只要两装饰器顺序不同,那么结果就会不一样了。
注意这里是一个装饰器又被另外一个装饰器装饰,这样一层一层的嵌套。
能用类图:
场景:
1、需要动态扩展一个类的功能。(继承也可以扩展,就是改动比较多)
2、需要为一批兄弟类进行统一的扩展功能。
优点:
装饰类和被装饰的类分开发展,解耦。 动态扩展一个类的功能
继承关系的一种替代方案,一层一层的嵌套,返回的都是一个对象,好像动态生成了一个“链”
缺点:
多层的装饰比较复杂,层多了之后出现问题排序问题比较难。
在实际工作中很少用到。
jdk 中装饰器的使用:
1、FilterInputStream 类就是一个装饰器类。
写法:装饰器和要被装饰的类实现同样的接口,并且在装饰器中有一个接口的属性,用于接口实现的委派。在装饰器类中实现接口方法委派给接口的子类。
FilterInputStream 装修了InputStream 接口,那么所有的InputStream 的子类都可以通过这个装修器类来装饰,装饰后可以直接使用InputStream 接口方法调用。具体实现是InputStream 接口来实现的。
FilterInputStream 的每一个子类都是一个装饰器,对InputStream类的增强。
2、ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(filePath)));
ObjectInputStream 也是个装饰器,把FileInputStream 装饰成了ObjectInputStream 对象,从而把FileInputStream 实现增强为了对对象的流写入。