从源代码的角度理解Java设计模式的装饰模式

一、装饰模式介绍

修饰符模式定义:不改变原始对象的附加函数比生成子类更灵活。

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

优点:它能够在不改变原有对象的情况下动态扩展函数,使扩展函数按照期望的顺序执行,达到不同的效果。

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

类型:结构型。

类图:

源码分析中的典型应用

  • Java I/O 中的装饰者模式
  • Spring Session 中的装饰者模式
  • Mybatis 缓存中的装饰者模式

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

可以抽取出通用系统的安全性、日志、当前限制等独立于业务的代码,在控制器转换前后使用模板法模式可以部分解决上述问题。

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中以实现可插拔性。

三、源码中的装饰者模式

3.1、Java IO中是体现最明显的装饰者模式。

它是.stream(InputStream/OutputStream),bytestream(Reader/Writer)是类,InputStream中的输入,ReaderClass:

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

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> T get(Object key, Class<T> 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包下的类是装饰类。没有抽象装饰类。

猜你喜欢

转载自blog.csdn.net/sd09044901guic/article/details/84971495