从源码角度理解Java设计模式——装饰者模式 原 荐

栏目: 后端 · 发布时间: 6年前

内容简介:装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活。适用场景:动态的给一个对象添加或者撤销功能。优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个功能按想要的顺序执行,以实现不同效果。

一、饰器者模式介绍

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

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

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

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

类型:结构型。

类图:

从源码角度理解 <a href='https://www.codercto.com/topics/22013.html'>Java</a> 设计模式——装饰者模式 原 荐

源码分析中的典型应用

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

从源码角度理解Java设计模式——装饰者模式 原 荐

从源码角度理解Java设计模式——装饰者模式 原 荐

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

流名称 应用场景
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> 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包下的类为装饰类,这里没有抽象装饰类。

参考:

设计模式 | 装饰者模式及典型应用

《码农翻身》刘欣


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Introduction to Graph Theory

Introduction to Graph Theory

Douglas B. West / Prentice Hall / 2000-9-1 / USD 140.00

For undergraduate or graduate courses in Graph Theory in departments of mathematics or computer science. This text offers a comprehensive and coherent introduction to the fundamental topics of graph ......一起来看看 《Introduction to Graph Theory》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具