序:一个面试题作为开场
假设你是在一家专门提供在线(web)图片处理服务的公司, 安排你去设计开发一个框架:
允许每次可以处理多张图片, 每张图片各自有各自的效果.
允许在不改动代码的情况下,添加或者移除插件(插件: 我们可以认为是某种具体的操作,比如PS中的滤镜
等效果),你可以使用配置文件来配置需要启用的插件,也可以是同其他方法来管理这些插件的配置
同一张图片运行多个效果叠加
不需要你实现什么界面的东西(UI), 团队其他人会去做,你现在定义的API将会用在UI的开发上, UI原型如
下图所示,
这是我之前面试的时候遇到的面试题,这是很明显的指示面试者使用装饰模式去思考(当然这里面的坑很多,如对于框架的理解,单元测试,命名规范,反射等,对于专业的软件工程师,这些的都是很重要的技能);
这里的“添加或者移除插件”的插件其实就可以理解为装饰器。
1.定义
装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任,装饰者模式相比生成子类可以更灵活地增加功能。
如说,有一个为方法加入日志的功能,当你想要动态的去添加该功能的时候,你就需要考虑使用装饰模式了。
UML图如下:引用自 链接
2.简单的一个例子
public abstract class Run { public abstract void Go(); } public class LogDecorator : Run { protected Run run; public LogDecorator(Run run) { this.run = run; } public override void Go() { //写日志 if (run != null) { run.Go(); } } } public class ErrorDecorator : Run { protected Run run; public ErrorDecorator(Run run) { this.run = run; } public override void Go() { // 错误处理装饰 try { if (run != null) { run.Go(); } } catch (Exception e) { } } } public class Main : Run { public override void Go() { //do something } }
这里的LogDecorator和ErrorDecorator都是装饰类,通过在类中添加Run的引用,并继承Run,使得装饰类有了两大武器。
1)类中的Run引用,可以调用需要装饰的方法。
2)继承Run,可以使装饰类转换成被被装饰类,从而实现叠加装饰
比如说,我想给Main中的Go方法添加日志以及异常处理我们只需要:
Run run = new Main(); Run logRun = new LogDecorator(run); Run LogAndErrRun = new ErrorDecorator(logRun); LogAndErrRun.Go();
这样就可以做到面试题中说明的“效果可以叠加”。
3.装饰模式的特点
优点:可以动态的为功能添加装饰功能,只需要添加对应的装饰类就可以了
缺点:被装饰类中的被装饰方法必须提取到一个抽象类或接口中,设计比较繁琐。而且或因此产生许多装饰类