一.装修人模式简介
装饰者模式定义:在不改变原始对象的情况下附加功能比生成子类更灵活。
适用的场景:动态地向对象添加或撤消功能。
优点:可以在不改变原始对象的情况下动态扩展功能。 能够按所需顺序执行扩展的多个功能,实现不同的效果。
缺点:更多的类使程序变得复杂
类型:结构类型。
类图:
源代码分析中的典型应用
Java I/O的装饰者模式
Spring Session的装饰者模式
Mybatis缓存中的装饰者模式
二.系统增加日志,安全、限流示例
可以提取一般系统的安全性、日志、流限制等与业务无关的代码,在Controller前后用切面进行改造。 模板方法模型可以部分解决这个问题。
公共类基础辅助{
logger logger=logger factory.getlogger (base command.class );
公共语音执行
//记录日志
Logger.debug(.start . );
//过滤跨站点脚本攻击
paramXssAspect (;
//限制费率
doRateLimit (;
doBusiness (;
Logger.debug(…end…);
}
公共业务语音业务(;
}
classplaceorderaspectextendsbaseaspect {
@Override
公共void do business (
//订单操作
}
}
classpayorderaspectextendsbaseaspect {
@Override
公共void do business (
//支付操作
}
}
父母班已经写了“一团糟”的非业务代码。 只需保留一个抽象方法等子类进行实现。 副班很干净。 只要关注业务逻辑就可以了。
这种方式的最大缺点是父类定义一切。 要执行这些非业务代码,子类只能无条件接受,比如按什么顺序执行。 如果有不限制速度的子类,就不能将其删除。
利用装饰者模型,可以灵活地解决上述问题。
//顶级抽象组件
接口I aspect {
字符串下载错误(;
}
//基本上是装饰过的类,做一些公共处理
classaspectimplimplementsiaspect {
@Override
公共字符串下载代理(
return '裸体跑步代码. ';
}
}
abstractclassabstractdecoratorimplementsiaspect {
//很重要,将抽象组件组合到自己的类中
私有I aspect aspect;
用publicabstractdecorator (iaspectaspect ) IAspect做自己
this.aspect=aspect;
}
@Override
公共字符串下载代理(
return this.aspect.dohandleraspect (;
}
}
日志添加、安全和流限制功能:
classloggeraspectextendsabstractdecorator {
publicloggeraspect { iaspectaspect }
辅助(aspect );
}
@Override
公共字符串下载代理(
return super.doHandlerAspect ('日志.';
}
}
classparamxssaspectextendsabstractdecorator {
publicparamxssaspect (iaspectaspect ) {
辅助(aspect );
}
@Override
公共字符串下载代理(
return super.doHandlerAspect()+"+过滤危险字符.";
}
}
class LimitAspect extends AbstractDecorator{
public LimitAspect(IAspect aspect){
super(aspect);
}
@Override
public String doHandlerAspect() {
return super.doHandlerAspect()+"+限流.";
}
}
测试一下:
public class Test {
public static void main(String[] args) {
IAspect aspect = new LimitAspect(new ParamXssAspect(new LoggerAspect(new AspectImpl())));
System.out.println(aspect.doHandlerAspect());
}
}
运行结果:
------
裸跑代码.+记录日志.+过滤危险字符.+限流.
------
通过上面可以看出,装饰者模式可以任意次序组装功能,是不是很灵活?另外,也可以把上述三个功能封装成注解@Log、@ParamXss、@AccessLimit,实现可拔插。如果读者想看注解功能完整实现,可以参考我的这个项目: SpringBoot+JWT+Shiro+MybatisPlus实现Restful快速开发后端脚手架
三、源码中的装饰者模式
3.1、Java IO中是体现最明显的装饰者模式。
它基于字符流(InputStream/OutputStream) 和 字节流(Reader/Writer)作为基类,下面画出InputStream、Reader的部分类图:
这里总结几种常用流的应用场景:
流名称
应用场景
ByteArrayInputStream
访问数组,把内存中的一个缓冲区作为 InputStream 使用,CPU从缓存区读取数据比从存储介质的速率快10倍以上
StringBufferInputStream
把一个 String 对象作为。InputStream。不建议使用,在转换字符的问题上有缺陷
FileInputStream
访问文件,把一个文件作为 InputStream ,实现对文件的读取操作
PipedInputStream
访问管道,主要在线程中使用,一个线程通过管道输出流发送数据,而另一个线程通过管道输入流读取数据,这样可实现两个线程间的通讯
SequenceInputStream
把多个 InputStream 合并为一个 InputStream . “序列输入流”类允许应用程序把几个输入流连续地合并起来
DataInputStream
特殊流,读各种基本类型数据,如byte、int、String的功能
ObjectInputStream
对象流,读对象的功能
PushBackInputStream
推回输入流,可以把读取进来的某些数据重新回退到输入流的缓冲区之中
BufferedInputStream
缓冲流,增加了缓冲功能
3.2、Spring Session中的ServletRequestWrapper(Response也一样)的装饰者模式。
public class ServletRequestWrapper implements ServletRequest {
private ServletRequest request;//组合抽象接口到自己的类中
public ServletRequestWrapper(ServletRequest request) {//可以构造自己
if(request == null) {
throw new IllegalArgumentException("Request cannot be null");
} else {
this.request = request;
}
}
public ServletRequest getRequest() {
return this.request;
}
public void setRequest(ServletRequest request) {
if(request == null) {
throw new IllegalArgumentException("Request cannot be null");
} else {
this.request = request;
}
}
//省略...
}
3.3、Spring Cache中的TransactionAwareCacheDecorator的装饰者模式。
其实从类名就可以看出。
public class TransactionAwareCacheDecorator implements Cache {
private final Cache targetCache;//把Cache组合到自己类中
public TransactionAwareCacheDecorator(Cache targetCache) {//通过Cache构造自己
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}
public T get(Object key, Class type) {
return this.targetCache.get(key, type);
}
public void put(final Object key, final Object value) {
// 判断是否开启了事务
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 将操作注册到 afterCommit 阶段
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
public void afterCommit() {
TransactionAwareCacheDecorator.this.targetCache.put(key, value);
}
});
} else {
this.targetCache.put(key, value);
}
}
// ...省略...
}
3.4、Mybatis中的装饰者。
Cache为抽象构件类,PerpetualCache为具体构件类,decorators包下的类为装饰类,这里没有抽象装饰类。
参考:
《码农翻身》gldgq