设计模式之装饰器模式

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

文章目录

一、什么是装饰器模式

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

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

我们先来看下装饰器模式的通用 类 图:

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

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

  1. 创建一个抽象组件Component来规定被装饰对象的行为
c 复制代码
/**
 * 抽象组件
 *
 * @author zdp
 * @date 2022/9/3 17:48
 */
public abstract class Component {

    public abstract void execute();
}
  • 创建具体组件ConcreteComponent

c 复制代码
/**
 * 具体组件(需要被装饰的组件)
 *
 * @author zdp
 * @date 2022/9/3 17:48
 */
public class ConcreteComponent extends Component {

    @Override
    public void execute() {
        System.out.println("具体组件处理业务逻辑");
    }

}
  • 创建一个抽象装饰器Decorator

c 复制代码
/**
 * 抽象装饰器(继承、实现抽象组件,并持有抽象组件)
 *
 * @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();
    }
}
  • 创建具体装饰器ConcreteDecorator

c 复制代码
/**
 * 抽象装饰器(继承、实现抽象组件,并持有抽象组件)
 *
 * @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();
    }
}
  • 测试

c 复制代码
/**
 * 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
c 复制代码
/**
 * 抽象装饰器,持有并实现抽象组件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) { }

    // 其他实现方法省略....
}
  • 创建具体装饰器JsonLogger

c 复制代码
/**
 * 具体装饰器: 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());
    }

}
  • 构造一个Factory

c 复制代码
/**
 * JsonLoggerFactory
 *
 * @author zdp
 * @date 2022/9/4 22:28
 */
public class JsonLoggerFactory {

    public static JsonLogger getLogger(Class<?> clazz){
        return new JsonLogger(LoggerFactory.getLogger(clazz));
    }

}
  • 测试

c 复制代码
/**
 * 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日志打印....");

    }
}

五、装饰器模式优缺点

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

五、Springboot下使用装饰者模式

c 复制代码
public interface PointsService {
    void addPoints(Long userId, int points);
}


@Service("corePointsService")
public class CorePointsService implements PointsService {
    @Override
    public void addPoints(Long userId, int points) {
        // 核心业务
    }
}

public class LoggingPointsServiceDecorator implements PointsService {

    private final PointsService delegate;

    public LoggingPointsServiceDecorator(PointsService delegate) {
        this.delegate = delegate;
    }

    @Override
    public void addPoints(Long userId, int points) {
        long start = System.currentTimeMillis();
        try {
            delegate.addPoints(userId, points);
        } finally {
            System.out.println("addPoints cost = " + (System.currentTimeMillis() - start));
        }
    }
}


@Configuration
public class PointsServiceConfig {

    @Bean
    @Primary
    public PointsService pointsService(
            @Qualifier("corePointsService") PointsService corePointsService) {
        return new LoggingPointsServiceDecorator(corePointsService);
    }
}
相关推荐
RainCityLucky4 分钟前
Java Swing 自定义组件库分享(七)
java·笔记·后端
小白|16 分钟前
cmake:昇腾CANN构建系统完全指南
java·c++·算法
weixin_5129761721 分钟前
Java 面试宝典 Beta5.0
java
Ting-yu24 分钟前
Spring AI Alibaba零基础速成(5) ---- Memory(记忆)
java·人工智能·后端·spring
月落归舟27 分钟前
一文掌握Spring AOP:从入门到底层原理
java·后端·spring
QuZhengRong29 分钟前
【Luck-Report】缓存
java·前端·后端·vue·excel
看山是山_Lau33 分钟前
建造者模式:复杂对象如何一步步构建
设计模式·建造者模式
XiYang-DING41 分钟前
【Spring】SpringMVC
java·后端·spring
想学习java初学者43 分钟前
SpringBoot整合GS1编码解码
java·spring boot·后端
日月云棠43 分钟前
2 快速入门实战指南
java·后端