设计模式之装饰器模式 详解
文章目录
一、什么是装饰器模式
装饰器模式(Decorator Pattern) 也称为包装模式(Wrapper Pattern) 是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态地扩展类的功能。
二、装饰器模式的角色组成
我们先来看下装饰器模式的通用 类 图:

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