设计模式之装饰器模式详解

一、什么是装饰器模式

装饰器模式(Decorator Pattern) 也称为包装模式(Wrapper Pattern) 是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态地扩展类的功能。

二、装饰器模式的角色组成

我们先来看下装饰器模式的通用类图:
在这里插入图片描述

  • 抽象组件(Component): 可以是一个接口或者抽象类,其充当被装饰类的原始对象,规定了被装饰对象的行为;
  • 具体组件(ConcreteComponent): 实现/继承Component的一个具体对象,也即被装饰对象;
  • 抽象装饰器(Decorator): 通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component抽象组件;其实现一般是一个抽象类,主要是为了让其子类按照其构造形式传入一个Component抽象组件,这是强制的通用行为(当然,如果系统中逻辑单一,并不需要实现许多装饰器,那么我们可以直接省略该类,而直接实现一个具体装饰器(ComcreteDecorator)即可);
  • 具体装饰器(ConcreteDecorator): Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能;

三、装饰器模式通用写法示例

  1. 创建一个抽象组件Component来规定被装饰对象的行为

    /**
     * 抽象组件
     *
     * @author zdp
     * @date 2022/9/3 17:48
     */
    public abstract class Component {
    
        public abstract void execute();
    }
    
  2. 创建具体组件ConcreteComponent

    /**
     * 具体组件(需要被装饰的组件)
     *
     * @author zdp
     * @date 2022/9/3 17:48
     */
    public class ConcreteComponent extends Component {
    
        @Override
        public void execute() {
            System.out.println("具体组件处理业务逻辑");
        }
    
    }
    
  3. 创建一个抽象装饰器Decorator

    /**
     * 抽象装饰器(继承、实现抽象组件,并持有抽象组件)
     *
     * @author zdp
     * @date 2022/9/3 17:49
     */
    public abstract class Decorator extends Component {
    
        /**
         * 抽象组件
         */
        public Component component;
    
        public Decorator(Component component) {
            this.component = component;
        }
    
        /**
         * 将执行动作转发给组件本身执行,可以在转发前后做装饰
         *
         * @author zdp
         * @date   2022/9/3 17:56
         */
        public void execute() {
            component.execute();
        }
    }
    
  4. 创建具体装饰器ConcreteDecorator

    /**
     * 具体装饰器A继承抽象装饰器
     *
     * @author zdp
     * @date 2022/9/3 17:49
     */
    public class ConcreteDecorator extends Decorator {
    
        public ConcreteDecorator(Component component) {
            super(component);
        }
    
        /**
         * 具体组件动作执行前的装饰
         *
         * @author zdp
         * @date   2022/9/3 17:58
         */
        public void before(){
            System.out.println("ConcreteDecorator前置操作....");
        }
    
        /**
         * 具体组件动作执行后的装饰
         *
         * @author zdp
         * @date   2022/9/3 17:58
         */
        public void after(){
            System.out.println("ConcreteDecorator后置操作....");
        }
    
        /**
         * 组件本身执行的动作
         *
         * @author zdp
         * @date   2022/9/3 18:01
         */
        public void execute() {
            before();
            component.execute();
            after();
        }
    }
    
    
  5. 测试

    /**
     * decorator 通用写法测试
     *
     * @author zdp
     * @date 2022/9/3 17:50
     */
    public class Test {
        public static void main(String[] args) {
            //创建需要被装饰的组件
            Component component = new ConcreteComponent();
            //给对象透明的增加功能并调用
            Decorator decorator = new ConcreteDecorator(component);
            decorator.execute();
        }
    }
    

四、装饰器模式业务中的应用举例

需求:现系统中采用slf4j打印的日志为字符串格式,现使用装饰器模式将日志打印输出为Json格式

现系统中的存在Logger接口以及Logger的实现,Logger就可视为抽象组件,Logger的具体实现就为具体组件,现我们只需完成抽象装饰器及具体的装饰器实现即可

  1. 创建抽象装饰器DecoratorLogger

    /**
     * 抽象装饰器,持有并实现抽象组件Logger
     *
     * @author zdp
     * @date 2022/9/3 18:44
     */
    public abstract class DecoratorLogger implements Logger {
    
        protected Logger logger;
    
        public DecoratorLogger(Logger logger) {
            this.logger = logger;
        }
    
        @Override
        public void info(String s) { }
    
        @Override
        public void error(String s) { }
    
        // 其他实现方法省略....
    }
    
  2. 创建具体装饰器JsonLogger

    /**
     * 具体装饰器: Json-logger
     *
     * @author zdp
     * @date 2022/9/3 18:47
     */
    public class JsonLogger extends DecoratorLogger {
    
        public JsonLogger(Logger logger) {
            super(logger);
        }
    
        @Override
        public void info(String message) {
            JSONObject obj = new JSONObject();
            obj.put("message", message);
            logger.info(obj.toString());
        }
    
        @Override
        public void error(String message) {
            JSONObject obj = new JSONObject();
            obj.put("message", message);
            logger.error(obj.toString());
        }
    
    }
    
  3. 构造一个Factory

    /**
     * JsonLoggerFactory
     *
     * @author zdp
     * @date 2022/9/4 22:28
     */
    public class JsonLoggerFactory {
    
        public static JsonLogger getLogger(Class<?> clazz){
            return new JsonLogger(LoggerFactory.getLogger(clazz));
        }
    
    }
    
  4. 测试

    /**
     * JsonLogger Test
     * 
     * @author zdp
     * @date 2022/9/3 17:50
     */
    public class Test {
    
        private static final Logger logger = LoggerFactory.getLogger(Test.class);
        private static final Logger jsonLogger = JsonLoggerFactory.getLogger(Test.class);
    
        public static void main(String[] args) {
    
            logger.info(" logger info 日志打印....");
            jsonLogger.info(" jsonLogger info 日志打印....");
            System.out.println();
            logger.error("logger error日志打印....");
            jsonLogger.error("jsonLogger error日志打印....");
    
        }
    }
    

在这里插入图片描述

五、装饰器模式优缺点

  • 优点
    • 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用
    • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
    • 装饰器完全遵守开闭原则
  • 缺点
    • 从代码层面来看,使用装饰器模式会出现更多的代码,更多的类,增加程序复杂性
    • 动态装饰时,多层装饰时会更复杂
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐