作者 | 北极星小组

想要写好代码,设计模式(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 ----------

举荐浏览【技术加油站】系列:

百度工程师教你玩转设计模式(工厂模式)

百度工程师教你玩转设计模式(适配器模式)

百度工程师教你玩转设计模式(单例模式)