java装饰者模式源代码_从源码角度理解Java设计模式——装饰者模式

一、饰器者模式介绍

装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活。

适用场景:动态的给一个对象添加或者撤销功能。

优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个功能按想要的顺序执行,以实现不同效果。

缺点:更多的类,使程序复杂

类型:结构型。

类图:

93c5df0b8fcdb8c5d4c49077b3f7a533.png

源码分析中的典型应用

Java I/O 中的装饰者模式

Spring Session 中的装饰者模式

Mybatis 缓存中的装饰者模式

二、给系统添加日志,安全、限流示例

一般系统的安全、日志、限流等业务无关代码可以抽离出来,在Controller前后用切面改造,模板方法模式可以部分解决这个问题:

public abstract class BaseAspect {

Logger logger = LoggerFactory.getLogger(BaseCommand.class);

public void execute(){

//记录日志

logger.debug("..start..");

//过滤跨站脚本攻击

paramXssAspect();

//限制速率

doRateLimit();

doBusiness();

logger.debug("..end..");

}

public abstract void doBusiness();

}

class PlaceOrderAspect extends BaseAspect {

@Override

public void doBusiness() {

//下单操作

}

}

class PayOrderAspect extends BaseAspect {

@Override

public void doBusiness() {

//支付操作

}

}

在父类中已经把”乱七八糟“的非业务代码写好了,只留了一个抽象方法等子类去实现,子类变的很清爽,只需关注业务逻辑就可以了。

这种方式最大的缺陷就是父类会定义一切:要执行那些非业务代码,以什么顺序执行等等,子类只能无条件接受。如果有一个子类,不限制速率,那么它也没有办法把它去掉。

利用装饰者模式,针对上面的问题,可以变的很灵活。

//最高层抽象组件

interface IAspect {

String doHandlerAspect();

}

//基本被装饰类

class AspectImpl implements IAspect{

@Override

public String doHandlerAspect() {

return "裸跑代码.";

}

}

abstract class AbstractDecorator implements IAspect{

//很重要,组合抽象构件到自己的类中

private IAspect aspect;

public AbstractDecorator(IAspect aspect) {//通过IAspect构造自己

this.aspect = aspect;

}

@Override

public String doHandlerAspect() {

return this.aspect.doHandlerAspect();

}

}

附加记录日志,安全,限流功能:

class LoggerAspect extends AbstractDecorator{

public LoggerAspect(IAspect aspect){

super(aspect);

}

@Override

public String doHandlerAspect() {

return super.doHandlerAspect()+"+记录日志.";

}

}

class ParamXssAspect extends AbstractDecorator{

public ParamXssAspect(IAspect aspect){

super(aspect);

}

@Override

public String doHandlerAspect() {

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的部分类图:

c92f234b4064f00e48b801e9b55bd608.png

8ca52211c8af26f17670ca8328745f3b.png

这里总结几种常用流的应用场景:

流名称

应用场景

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包下的类为装饰类,这里没有抽象装饰类。

f60857fc498b857cdb15eecfc15006e6.png

参考:

《码农翻身》刘欣

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值