Decorator模式定义:动态给一个对象添加一些额外的职责,它是通过创建一个包装对象,也就是装饰来包裹真实的对象。就像在墙上刷油漆,使用Decorator模式相比使用生成子类方式达到功能的扩充更加灵活。
为什么使用Decorator:我们通常可以使用继承来实现功能的扩展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性。同时,使用继承实现功能的拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定的,是静态的。使用Decorator的理由:Decorator提供了“即插即用”的方法,在运行期间决定何时增加何种功能。
装饰模式在Java种使用很广泛,比如我们在重新定义按钮、对话框等时候,实际上已经在使用装饰模式了。装饰模式使用被装饰类的一个子类的实例,把客户端的调用委派到被装饰类,装饰模式的关键在于这种扩展是完全透明的。
原理图
原理图
其中类的职责如下:
抽象构件角色(Project):给出一个接口,以规范准备接收附加责任的对象
具体构件角色(Employe):定义一个将要接收附加责任的类
装饰角色(Manager):持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口
具体装饰角色(ManagerA、ManagerB):负责给构件对象“贴上”附加的责任
下面通过一个软件项目例子来说明装饰模式的使用
过程是这样的:
项目经理接到一个项目,项目最终要完成编码。
项目经理接到项目后,先做些前期的工作(比如需求分析、设计),然后将编码工作委派给代码工人,代码工人干完后,项目经理做项目的收尾工作。
public interface Project {
void doCoding();//写代码
}
public class Employe implements Project{
public void doCoding(){
System.out.println("代码工人在痛苦的写代码");
}
}
public class Manager implements Project {
private Project project;
public Manager(Project project) {
this.project = project;
}
public void doCoding() {
startNewWork();
}
public void startNewWork() {
doEarlyWork();
project.doCoding();
doEndWork();
}
public void doEarlyWork() {
}
public void doEndWork() {
}
}
public class ManagerA extends Manager{
public ManagerA(Project project) {
super(project);
}
public void doEarlyWork() {
System.out.println("项目经理A 在做需求分析");
System.out.println("项目经理A 在做架构设计");
System.out.println("项目经理A 在作详细设计");
}
}
public class ManagerB extends Manager {
public ManagerB(Project project) {
super(project);
}
public void doEarlyWork() {
System.out.println("项目经理B 在作需求分析");
System.out.println("项目经理B 在做详细设计");
}
public void doEndWork() {
System.out.println("项目经理B 在做收尾工作");
}
}
public class Client {
public static void main(String args[]) {
Project employe = new Employe();
Project managerA = new ManagerA(employe);
Project managerB = new ManagerB(employe);
managerA.doCoding();
managerB.doCoding();
}
}
运行结果:
项目经理A 在做需求分析
项目经理A 在做架构设计
项目经理A 在做详细设计
代码工人 在编写代码,加班编啊编啊,终于编完了!
项目经理B 在做需求分析
项目经理B 在做详细设计
代码工人 在编写代码,加班编啊编啊,终于编完了!
项目经理B 在做收尾工作
装饰模式的特点;
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的索引(reference)
(3) 装饰对象接受所有的来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构 就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
装饰模式 VS 继承
装饰模式 继承
用来扩展特定对象的功能 用来扩展一类对象的功能
不需要子类 需要子类
动态地 静态地
运行时分配职责 编译时分派职责
防止由于子类而导致的复杂和混乱 导致很多子类产生,在一些场合,报漏类的层次
更多的灵活性 缺乏灵活性
对于一个给定的对象,同时可能有
不同的装饰对象,客户端可以通过
它的需要选择合适的装饰对象发送
消息。 对于所有可能的联合,客户期望
很容易增加任何的 困难