本文共 4486 字,大约阅读时间需要 14 分钟。
修饰符模式定义:不改变原始对象的附加函数比生成子类更灵活。
适用场景:动态的给一个对象添加或者撤销功能。
优点:它能够在不改变原有对象的情况下动态扩展函数,使扩展函数按照期望的顺序执行,达到不同的效果。
缺点:更多的类,使程序复杂
类型:结构型。
类图:
源码分析中的典型应用
可以抽取出通用系统的安全性、日志、当前限制等独立于业务的代码,在控制器转换前后使用模板法模式可以部分解决上述问题。
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; } publicT 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包下的类是装饰类。没有抽象装饰类。
转载地址:http://fzgbi.baihongyu.com/