背景
我们在开发过程中,如果我们想使用存在的轮子,但是轮子的有些功能不能满足我们。我们需要对轮子新增想要的功能。好的设计方式是不改变轮子的结构,动态的扩展功能。装饰模式就是这样的一种设计方式。
什么是装饰模式
“
装饰模式(Decorator Pattern)是一种比较常见的模式,其定义如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)
”
是不是觉得跟代理模式有些像呢?对代理模式感兴趣的可以看看我的这篇文章:
别急,我们继续往下看。
装饰模式的主要组成要素为:
“抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。”
要素之间的关系如下:
装饰模式
示例代码
Component
public interface Component {
void operation();
}
ConcreteComponent
public class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
@Override
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
Decorator
public class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
ConcreteDecorator
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
测试代码如下:
public class DecoratorTest {
public static void main(String[] args){
Component p = new ConcreteComponent();
p.operation();
System.out.println("------------------");
Component concreteDecorator = new ConcreteDecorator(p);
concreteDecorator.operation();
}
}
如上代码,我们想使用具体构件(即文章开头的轮子)的operation(),并且想对operation()方法增加一些功能,我们构建了一个装饰对象,这个对象包裹这具体构件,最后我们调用了ConcreteDecorator#operation(),这个方法就是增强后的方法。
测试结果:
-------------------
调用具体构件角色的方法operation()
------------------
调用具体构件角色的方法operation()
为具体构件角色增加额外的功能addedFunction()
我们对operation()方法增加了功能:
为具体构件角色增加额外的功能addedFunction()
关于装饰模式的思考
如果我们需要对当前类添加额外的职责,但是又不能通过生成当前类的子类去扩充时,就可以使用装饰模式了。
装饰者模式的优势在于可以动态扩展新功能,也可以动态撤销新功能,还可以将多组功能排列组合生成新功能。
其实Java中的InputStream的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream 等,它们都是抽象装饰类。
BufferedReader in=new BufferedReader(new FileReader("lvshen_av.txt));
String s=in.readLine();
如上代码就使用了装饰模式。
对于构件Component和装饰者Decorator来说,它们之间不相互耦合。装饰者是从外部扩展构件的功能,不需要知道构件的具体结构,不需要改变构件。
通俗点就是:
史塔克穿上钢铁侠战甲就变成钢铁侠,同样他的女友小辣椒也能穿上钢铁侠战甲变成钢铁侠。钢铁侠战甲不关注谁穿上了它。不同的人穿上就成了不同的钢铁侠。
但是只要注意不要多层装饰,这样会显得系统很复杂。
例如,史塔克穿上钢铁侠战甲成为钢铁侠,钢铁侠穿上反浩克战甲。虽然力量变强了,但是操作难度也变高了。
关于装饰模式与代理模式
两者都是对功能增强。面试会经常问这两点区别。
我认为,装饰器模式关注于在一个对象上动态的添加方法,代理模式关注于控制对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
你还可以这样理解:
“装饰模式中史塔克可以穿上钢铁侠战甲,女友小辣椒也能装上钢铁侠战甲。代理模式中史塔克和钢铁侠战甲已经绑死了,战甲也焊死了,小辣椒没法穿这套战甲了。”
如图,左边为代理模式,右边为装饰模式。
左边为代理模式的调用,这里我们不需要传入目标类,目标类对象在代理类内部生成。
右边为装饰模式的调用,目标类对象需要在这里传入装饰类中。
至于优缺点,大家可以自己思考下它们的使用场景。