作者 | 北极星小组
想要写好代码,设计模式(Design Pattern)是必不可少的基本功,设计模式是对面向对象设计(Object Oriented Design)中重复呈现的一类问题的一种解决方案,本篇介绍装璜器模式(Decorator Pattern)。
在咱们日常的开发过程中,一个最常见的场景就是在已有的根底上新增性能,惯例的做法有以下几种:
- 批改已有的类:违反开闭准则。
- 减少新的子类:每次都得新增大量对应的类,随着性能的减少,子类越来越收缩。
在此场景下,装璜器模式就能够体现出它的劣势了,它容许在不批改原有对象的前提下,灵便的扩大已有类的性能。上面是装璜器模式的一个通用的类图:
△UML
其中的各个类的作用如下:
- 形象组件(Component): 能够是接口或者抽象类,它定义了具体类以及装璜器所领有的办法。
- 具体组件(ComponentA, ComponentB):具体的组件,实现或者继承自形象组件。能够了解成上述场景中已存在的类。
- 形象装璜器(Decorator): 通常为抽象类,持有一个被装璜的对象,定义了具体装璜器的办法。此类非必须也能够没有,具体装璜器也可间接继承或者实现形象组件。
- 具体装璜器(DecoratorX, DecoratorY): 具体的装璜器,继承自形象装璜器(也可间接继承自形象组件),扩大了形象组件的某些性能。
上面,将通过3个具体的案例的解说装璜器的应用形式,不便大家进一步的了解。
一、装璜器在工作解决场景的利用
在理论的开发中,咱们常常须要定义不同的类来解决各种不同的工作。假如一个这样的场景,咱们的零碎有多个具体的类,用来解决不同类型的工作。当初须要增加一个性能,就是在解决完工作后收回一条音讯。针对这个场景,应用装璜器模式的实现思路如下:
- 形象组件(TaskProcessor):解决工作的抽象类(亦可通过接口实现),定义一个通用的工作解决办法process()。
- 具体组件(TaskProcessorA, TaskProcessorB): 负责实现具体的工作解决逻辑
- 形象装璜器(TaskProcessDecorator):持有一个工作解决对象实例
- 具体装璜器(AfterTaskProcessDecorator):实现具体的工作解决实现后的音讯告诉扩大能力
具体的代码如下:
package com.baidu.demo;public class Decorator { // 形象组件 static abstract class TaskProcessor { abstract void process(); } // 具体组件 static class TaskProcessorA extends TaskProcessor { @Override void process() { System.out.println("TaskProcessorA解决实现"); } } // 具体组件 static class TaskProcessorB extends TaskProcessor { @Override void process() { System.out.println("TaskProcessorB解决实现"); } } // 形象装璜器 static abstract class TaskProcessDecorator extends TaskProcessor { protected TaskProcessor processor; public TaskProcessDecorator(TaskProcessor processor) { this.processor = processor; } abstract void process(); } // 具体装璜器 static class AfterTaskProcessDecorator extends TaskProcessDecorator { public AfterTaskProcessDecorator(TaskProcessor processor) { super(processor); } @Override void process() { processor.process(); afterProcess(); } void afterProcess() { System.out.println("工作处理完毕,发送音讯..."); } } public static void main(String[] args) { // 扩大之前 System.out.println("==========before=========="); TaskProcessor processorA = new TaskProcessorA(); processorA.process(); TaskProcessor processorB = new TaskProcessorB(); processorB.process(); // 装璜器扩大之后:TaskProcessorA TaskProcessorB并未做任何批改,即可实现性能的扩大 System.out.println("==========after=========="); TaskProcessor decoratorA = new AfterTaskProcessDecorator(processorA); decoratorA.process(); TaskProcessor decoratorB = new AfterTaskProcessDecorator(processorB); decoratorB.process(); }}// 输入后果如下==========before==========TaskProcessorA解决实现TaskProcessorB解决实现==========after==========TaskProcessorA解决实现工作处理完毕,发送音讯...TaskProcessorB解决实现工作处理完毕,发送音讯...
二、装璜器在文件IO场景的利用
装璜器模式,一个典型的利用就是文件IO操作,最根底的类实现字节流读取类,应用装璜器模式能够封装文件字节流读取类,而后能够持续封装可缓存的文件字节流读取类,在我的项目中按需应用。具体实现如下:
- InputStream:具体组件,实现读取字节流。
- FileInputStream:具体装璜器,作为InputStream的子类,扩大文件操作。
- BufferedInputStream:具体装璜器,作为FileInputStream的子类,扩大缓存操作。
具体代码如下:
//具体组件,实现读取字节流public abstract class InputStream { public int read(byte b[], int off, int len) {}}//具体装璜器,作为InputStream的子类,扩大文件操作public class FileInputStream extends InputStream { protected InputStream in; public FileInputStream(String name) { InputStream in = ... //此处省略,通过文件名创建对象 this.in = in; } public int read(byte b[], int off, int len) { return this.in.read(b, off, len); }}//具体装璜器,作为FileInputStream的子类,扩大缓存操作public class BufferedInputStream extends FileInputStream { protected FileInputStream in; protected byte[] buffer; public BufferedInputStream(FileInputStream in) { this.in = in; } public int read(byte b[], int off, int len) { if (this.buffer == null || this.buffer.length == 0) { this.in.read(this.buffer, 0, in.lenght()); } System.arraycopy(this.buffer, off, b, 0, len); ... }}public static void main(String[] args) { FileInputStream fs = new FileInputStream('./test.log'); BufferedInputStream bs = new BufferedInputStream(fs); byte[] b; bs.read(b, 0, 1);}
三、装璜器在日志零碎场景的利用
在日志零碎中,个别罕用日志的级别别离为 DEBUG(调试)、INFO(运行信息)、WARN(正告)、ERROR(谬误),一旦产生谬误级别的日志后,则须要触发报警告诉相干人员及时进行跟进,报警形式个别有:邮件、短信、如流等,通常咱们会依据业务场景以组合的形式进行报警告诉,应用装璜器模式则能很好实现组合报警这一性能。
- 形象组件:Log接口形象
- 具体组件:Slf4j 具体日志类的实现
- 形象装璜器:LogDecorator 日志装璜器的基类
- 具体装璜器:MailLogDecorator、SMSLogDecorator、InfoFlowLogDecorator具体装璜类
/** * 日志接口 */public interface Log { void debug(String message); void info(String message); void warn(String message); void error(String message);}/** * Slf4j 日志 */public class Slf4jLog implements Log { //日志记录对象 private final Logger log = LoggerFactory.getLogger("system_log"); @Override public void debug(String message) { if (log.isDebugEnabled()) { log.debug(message); } } @Override public void info(String message) { if (log.isInfoEnabled()) { log.info(message); } } @Override public void warn(String message) { if (log.isWarnEnabled()) { log.warn(message); } } @Override public void error(String message) { if (log.isErrorEnabled()) { log.error(message); } }}/** * 日志装璜器 */public class LogDecorator implements Log { protected Log log; public LogDecorator(Log log) { this.log = log; } @Override public void debug(String message) { log.debug(message); } @Override public void info(String message) { log.info(message); } @Override public void warn(String message) { log.warn(message); } @Override public void error(String message) { log.error(message); }}/** * 邮件日志装璜器 */public class MailLogDecorator extends LogDecorator { public MailLogDecorator(Log log) { super(log); } @Override public void warn(String message) { log.warn(message); mail(message); } @Override public void error(String message) { log.error(message); mail(message); } public void mail(String message) { //模仿邮件发送 log.info("邮件已发送,信息:" + message); }}/** * 短信日志装璜器 */public class SMSLogDecorator extends LogDecorator { public SMSLogDecorator(Log log) { super(log); } @Override public void error(String message) { log.error(message); send(message); } public void send(String message) { //模仿短信发送 log.info("短信已发送,信息:" + message); }}/** * 如流日志装璜器 */public class InfoflowLogDecorator extends LogDecorator { public InfoflowLogDecorator(Log log) { super(log); } @Override public void warn(String message) { log.warn(message); send(message); } @Override public void error(String message) { log.error(message); send(message); } public void send(String message) { //模仿如流发送 log.info("如流音讯已发送,信息:" + message); }}/** * 日志测试类 */public class LogTest { /** * 测试日志装璜器 */ @Test public void testLogDecorator() { Log log = new SMSLogDecorator(new InfoFlowLogDecorator(new MailLogDecorator(new Slf4jLog()))); log.debug("零碎调试开启"); log.info("零碎失常运行"); log.warn("数据为空正告"); log.error("db 连贯谬误"); }}===========output=========15:16:56.564 [main] DEBUG system_log - 零碎调试开启15:16:56.566 [main] INFO system_log - 零碎失常运行15:16:56.566 [main] WARN system_log - 数据为空正告15:16:56.566 [main] INFO system_log - 邮件已发送,信息:数据为空正告15:16:56.566 [main] INFO system_log - 如流音讯已发送,信息:数据为空正告15:16:56.566 [main] ERROR system_log - db 连贯谬误15:16:56.566 [main] INFO system_log - 邮件已发送,信息:db 连贯谬误15:16:56.566 [main] INFO system_log - 如流音讯已发送,信息:db 连贯谬误15:16:56.566 [main] INFO system_log - 短信已发送,信息:db 连贯谬误Process finished with exit code 0
四、总结
如上几个案例,装璜器的最大作用就是在不批改原有类的根底上扩大已有的性能,它合乎开闭准则,而且实现也比拟灵便。
---------- END ----------
举荐浏览【技术加油站】系列:
百度工程师教你玩转设计模式(工厂模式)
百度工程师教你玩转设计模式(适配器模式)
百度工程师教你玩转设计模式(单例模式)