Fork me on GitHub

记一次装饰者模式的应用

设计模式大家可能都接触过,作为面向对象开发的圣经,我们都迫切的想掌握它,最近接触到了装饰者模式,于是我想着尽快掌握它,于是我在项目中打算使用它!

首先来了解下业务场景

我呢,之前在项目中使用到了ElasticSearch来作为项目的日志存储,这个日志模块还需要有个日志导出,这些功能我已经在项目中实现了,使用的是SpringData整合ElasticSearch(以下开始简称ES),我将ES日志的基础操作当作一个基础模块包common.elasticlog,将日志导出功能当作另一个基础模块包common.elasticexcel,更好的细分模块。

撞到了装饰者模式

突然,我想到了装饰者模式,装饰者模式的意图为“动态的给一个对象添加额外的职责”,那导出功能不就是日志的“额外职责”么,有了这个想法,我开始画出我的业务场景下的装饰者模式应用的结构图

结构图的产出是经过分析现有业务模块加上对装饰者模式的意图与使用场景结合的,我有一些记录总结,这些帮助我更好的去分析使用装饰者模式:
1.当需要给一个对象动态的添加职责的时候,需要装饰者模式,用它来代替继承。注意两个关键词“动态”、“代替继承”,这也恰恰表明了装饰者模式是来代替继承的方式来扩展对象的行为的,而且是运行时扩展。
2.定义了一个装饰者类(如图中的LogDecorator,它是个抽象类)继承我们的组件类(要被装饰的类,一般是接口),装饰者类的目的是要“达到类型一致”,而不是通过继承获取“行为”。就好像做陶器的模具,使用模具来塑造成一个具体的“类型”,关注点在于要做成的陶器类型,而具体的陶器“行为”可以参考一些现成品,模仿雕画出花纹或自己添加花纹等。
3.要添加给对象的新职责(或称为新行为),与装饰者对象是一对多的关系,也可以说这个行为是加给被装饰者类的任一对象(自身及其子类)。图中给日志对象添加的导出行为,是添加给了每个日志对象(操作日志、登陆日志、系统异常日志)。装饰者不需要感知到被装饰者有几个子类,可以说是给父类加行为(子类继承行为),这也体现了装饰者模式的使用方式,需要通过组合(区别于继承的一种功能复用的技术)公共的组件接口(要被装饰的类接口),而不是组合特定的具体组件(被装饰的类接口的实现类)。
4.怎么定义装饰者类,首先我们从上面第二点可以看出,我们的装饰者类只是用作“类型匹配”,那么定义一个(一个就够用)装饰者抽象类就可以达到类型匹配,如果有多个不同的行为扩展,定义多个不同的具体装饰者即可(继承装饰者抽象类,达到类型匹配)。一种特殊情况,如果只有一种行为扩展,我们可以直接定义一个具体的装饰者类继承于组件类,而不用定义一个公共的装饰者抽象类了,如图,其实我可以将LogDecorator抽象类给去掉。

实践是检验的唯一方式

我开始重构代码,添加装饰者抽象类,并定义了LogEnhanceExport具体的装饰者类,一切都很完美,现在开始使用它。
在日志控制器中,我使用@Autowired来注入LogDecorator,目前只存在一个具体装饰者类LogEnhanceExport,所以按类型来注入即可。注入是成功的,但我需要去动态的组合一个具体的组件类来实现行为,例如如下代码

点击查看代码
// 给登录日志组件加上导出行为
@Autowired
private LoginRepository loginRepository;
--snip--
logEnhanceExport.setBaseQueryRepository(loginRepository);
> 我注入了一个loginRepository,通过多态特性将它设置给logEnhanceExport中的BaseQueryRepository对象属性,选择具体的组件也可以通过泛型编程来实现多态,使用泛型的方式将会进行类型检查,更加安全,且代码量更少。如下代码所示:
点击查看代码
@Component
public class LogEnhanceExport<T extends BaseQueryRepository> extends LogDecorator<T> {
--snip--
}
> logEnhanceExport设置了具体的日志组件后,logEnhanceExport就是一个增强导出行为的日志对象,它仍可以完成日志组件的任何行为。
posted @ 2022-01-05 11:40  三脚半猫  阅读(82)  评论(0)    收藏  举报