作者|PolarisGroup想要写出好的代码,设计模式(DesignPattern)是必备的基本功,设计模式是一种面向对象设计(ObjectOrientedDesign),解决反复出现的一类问题),本文介绍装饰者模式。在我们日常的开发过程中,最常见的场景之一就是在已有的基础上增加新的功能。常规的方法有:修改已有的类:违反开闭原则。添加新的子类:每次都要添加大量对应的类。随着功能的增加,子类越来越膨胀。在这种场景下,装饰器模式就可以显示出它的优势,它允许在不修改原有对象的情况下,灵活地扩展现有类的功能。下面是装饰者模式的通用类图:△UML各类的作用如下:抽象组件(Component):可以是接口也可以是抽象类,定义了具体类和其拥有的方法装饰器。Concretecomponents(ComponentA,ComponentB):具体组件,实现或继承自抽象组件。可以理解为上面场景中已有的类。抽象装饰器(Decorator):通常是一个抽象类,它持有一个被装饰的对象,并定义了具体装饰器的方法。这种类型不是必须的也可以不是,具体的装饰器也可以直接继承或实现抽象组件。具体装饰器(DecoratorX,DecoratorY):具体装饰器,继承自抽象装饰器(或直接继承自抽象组件),扩展了抽象组件的一些功能。下面,将通过三个具体案例来讲解装饰器的使用,以方便大家进一步的理解。一、装饰器在任务处理场景中的应用在实际开发中,我们往往需要定义不同的类来处理各种不同的任务。假设我们的系统有多个处理不同类型任务的具体类。现在我们需要添加一个功能,就是任务处理完成后发送消息。对于这种场景,使用装饰器模式的实现思路如下:抽象组件(TaskProcessor):处理任务的抽象类(也可以通过接口实现),定义了通用的任务处理方法process().具体组件(TaskProcessorA、TaskProcessorB):负责实现具体的任务处理逻辑抽象装饰器(TaskProcessDecorator):持有一个任务处理对象实例具体装饰器(AfterTaskProcessDecorator):实现具体任务处理完成后的消息通知扩展能力代码如下如下:包com.baidu.demo;publicclassDecorator{//抽象组件staticabstractclassTaskProcessor{abstractvoidprocess();}//具体组件staticclassTaskProcessorAextendsTaskProcessor{@Overridevoidprocess(){System.out.println("TaskProcessorA处理完成");}}//具体组件静态类TaskProcessorBextendsTaskProcessor{@Overridevoidprocess(){System.out.println("TaskProcessorB处理完成");}}//抽象装饰器staticabstractclassTaskProcessDecoratorextendsTaskProcessor{protectedTaskProcessorprocessor;publicTaskProcessDecorator(TaskProcessorprocessor){this.processor=processor;}抽象无效过程();}//具体装饰器静态类AfterTaskProcessDecorator扩展TaskProcessDecorator{publicAfterTaskProcessDecorator(TaskProcessor处理器){超级(处理器);}@Overridevoidprocess(){processor.process();处理后();}voidafterProcess(){System.out.println("任务处理完毕,发送消息...");}}publicstaticvoidmain(String[]args){//展开前System.out.println("===========before==========");TaskProcessorprocessorA=newTaskProcessorA();processorA.process();TaskProcessorprocessorB=newTaskProcessorB();processorB.process();//装饰器扩展后:TaskProcessorATaskProcessorB没有被修改,可以扩展函数System.out.println("===========after==========");TaskProcessordecoratorA=newAfterTaskProcessDecorator(processorA);decoratorA.process();TaskProcessordecoratorB=newAfterTaskProcessDec演说家(处理器B);decoratorB.process();}}//输出结果如下===========before===========TaskProcessorA处理完成TaskProcessorB处理完成==========after==========TaskProcessorA完成任务处理,发送消息...TaskProcessorB完成任务处理,发送消息...2.装饰器在文件IO场景中的应用装饰器模式,典型应用是文件IO操作,最基础的类实现字节流读取类,使用装饰器模式可以封装文件字??节流读取类,然后继续封装可缓存的文件字节流读取类在项目中按需使用.具体实现如下:InputStream:具体组件,实现读取字节流。FileInputStream:具体装饰器,作为InputStream的子类,扩展文件操作。BufferedInputStream:具体装饰器,作为FileInputStream的子类,扩展缓存操作。具体代码如下://具体组件,实现读取字节流publicabstractclassInputStream{publicintread(byteb[],intoff,intlen){}}//具体装饰器,作为InputStream的子类,扩展文件操作publicclassFileInputStreamextendsInputStream{protectedInputStreamin;publicFileInputStream(Stringname){InputStreamin=...//此处省略,按文件名创建对象this.in=in;}publicintread(byteb[],intoff,intlen){returnthis.in.read(b,off,len);}}//具体装饰器,作为FileInputStream的子类,扩展缓存操作publicclassBufferedInputStreamextendsFileInputStream{protectedFileInputStreamin;受保护的字节[]缓冲区;publicBufferedInputStream(FileInputStreamin){this.in=in;}publicintread(byteb[],intoff,intlen){if(this.buffer==null||this.buffer.length==0){this.in.read(this.buffer,0,in.lenght());}System.arraycopy(this.buffer,off,b,0,len);...}}publicstaticvoidmain(String[]args){FileInputStreamfs=newFileInputStream('./test.log');BufferedInputStreambs=newBufferedInputStream(fs);字节[]b;bs.read(b,0,1);}系统场景应用在日志系统中,常用的日志级别有DEBUG(调试)、INFO(运行信息)、WARN(警告)、ERROR(错误)。一旦出现错误级别日志,需要触发告警通知,相关人员及时跟进。告警方式一般包括:邮件、短信,比如流媒体等,通常我们会根据业务场景,采用组合方式进行告警通知。使用装饰器模式可以很好的实现组合报警功能。抽象组件:Log接口抽象具体组件:Slf4j具体日志类实现抽象装饰器:LogDecorator日志装饰器基类具体装饰器:MailLogDecorator,SMSLogDecorator,InfoFlowLogDecorator具体装饰类/***日志接口*/publicinterfaceLog{voiddebug(String信息);无效信息(字符串消息);无效警告(字符串消息);voiderror(Stringmessage);}/***Slf4jlog*/publicclassSlf4jLogimplementsLog{//记录对象privatefinalLoggerlog=LoggerFactory.getLogger("system_log");@Overridepublicvoiddebug(Stringmessage){if(log.isDebugEnabled()){log.debug(message);}}@Overridepublicvoidinfo(Stringmessage){if(log.isInfoEnabled()){log.info(message);}}@Overridepublicvoidwarn(Stringmessage){if(log.isWarnEnabled()){log.warn(message);}}@Overridepublicvoiderror(Stringmessage){if(log.isErrorEnabled()){log.error(message);}}}/***日志Decorator*/publicclassLogDecorator实现Log{protectedLoglog;publicLogDecorator(Loglog){this.log=log;}@Overridepublicvoiddebug(Stringmessage){log.debug(message);}@Overridepublicvoidinfo(Stringmessage){log.info(message);}@Overridepublicvoidwarn(Stringmessage){log.warn(message);}@Overridepublicvoiderror(Stringmessage){log.error(message);}}/***邮件日志装饰器*/publicclassMailLogDecoratorextendsLogDecorator{publicMailLogDecorator(Loglog){super(log);}@Overridepublicvoidwarn(Stringmessage){log.warn(message);邮件(消息);}@Overridepublicvoiderror(Stringmessage){log.error(message);邮件(消息);}publicvoidmail(Stringmessage){//模拟邮件发送log.info("邮件已发送,信息:"+message);}}/***短信日志装饰器*/publicclassSMSLogDecoratorextendsLogDecorator{publicSMSLogDecorator(Loglog){super(log);}@Overridepublicvoiderror(Stringmessage){log.error(message);发信息);}publicvoidsend(Stringmessage){//模拟短信发送log.info("短信已发送,信息:"+message);}}/***比如流日志装饰器*/publicclassInfoflowLogDecoratorextendsLogDecorator{publicInfoflowLogDecorator(Loglog){super(log);}@Overridepublicvoidwarn(Stringmessage){log.warn(message);发信息);}@Overridepublicvoiderror(Stringmessage){log.error(message);发信息);}publicvoidsend(Stringmessage){//模拟流发送log.info("如果流消息已经发送,信息:"+message);}}/***日志测试类*/publicclassLogTest{/***测试日志装饰器*/@TestpublicvoidtestLogDecorator(){日志日志=newSMSLogDecorator(newInfoFlowLogDecorator(newMailLogDecoratorcorator(新Slf4jLog())));log.debug("系统调试已开启");log.info("系统运行正常");log.warn("数据为空警告");log.error("数据库连接错误");}}============输出==========15:16:56.564[main]DEBUGsystem_log-启用系统调试15:16:56.566[main]INFOsystem_log-系统运行正常15:16:56.566[main]WARNsystem_log-数据为空警告15:16:56.566[main]INFOsystem_log-邮件已发送,信息:数据为空警告15:16:56.566[main]INFOsystem_log-如果发送流消息,信息:数据为空警告15:16:56.566[main]ERRORsystem_log-db连接错误15:16:56.566[main]INFOsystem_log-消息已发送,信息:db连接错误15:16:56.566[main]INFOsystem_log-如果流消息已发送,信息:dbconnectionerror15:16:56.566[main]INFOsystem_log-SMS已发送,信息:dbconnectionerrorProcessfinishedwithexitcode04.Summary如以上案例,装饰器最大的作用就是在不修改原有类的情况下,扩展已有的功能。符合开闭原则,实现更灵活----------END----------推荐阅读【技术加油站】系列:百度工程师教你怎么玩设计模式(工厂模式)百度工程师教你怎么玩设计模式(适配模式)百度工程师教你玩设计模式(单例模式)
