一、饰器者模式介绍

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

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

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

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

类型:结构型。

类图:

源码分析中的典型应用

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

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

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


  1. public abstract class BaseAspect {
  2. Logger logger = LoggerFactory.getLogger(BaseCommand.class);
  3. public void execute(){
  4. //记录日志
  5. logger.debug("..start..");
  6. //过滤跨站脚本攻击
  7. paramXssAspect();
  8. //限制速率
  9. doRateLimit();
  10. doBusiness();
  11. logger.debug("..end..");
  12. }
  13. public abstract void doBusiness();
  14. }
  15. class PlaceOrderAspect extends BaseAspect {
  16. @Override
  17. public void doBusiness() {
  18. //下单操作
  19. }
  20. }
  21. class PayOrderAspect extends BaseAspect {
  22. @Override
  23. public void doBusiness() {
  24. //支付操作
  25. }
  26. }

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

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

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


  1. //最高层抽象组件
  2. interface IAspect {
  3. String doHandlerAspect();
  4. }
  5. //基本被装饰类
  6. class AspectImpl implements IAspect{
  7. @Override
  8. public String doHandlerAspect() {
  9. return "裸跑代码.";
  10. }
  11. }
  12. abstract class AbstractDecorator implements IAspect{
  13. //很重要,组合抽象构件到自己的类中
  14. private IAspect aspect;
  15. public AbstractDecorator(IAspect aspect) {//通过IAspect构造自己
  16. this.aspect = aspect;
  17. }
  18. @Override
  19. public String doHandlerAspect() {
  20. return this.aspect.doHandlerAspect();
  21. }
  22. }

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


  1. class LoggerAspect extends AbstractDecorator{
  2. public LoggerAspect(IAspect aspect){
  3. super(aspect);
  4. }
  5. @Override
  6. public String doHandlerAspect() {
  7. return super.doHandlerAspect()+"+记录日志.";
  8. }
  9. }
  10. class ParamXssAspect extends AbstractDecorator{
  11. public ParamXssAspect(IAspect aspect){
  12. super(aspect);
  13. }
  14. @Override
  15. public String doHandlerAspect() {
  16. return super.doHandlerAspect()+"+过滤危险字符.";
  17. }
  18. }
  19. class LimitAspect extends AbstractDecorator{
  20. public LimitAspect(IAspect aspect){
  21. super(aspect);
  22. }
  23. @Override
  24. public String doHandlerAspect() {
  25. return super.doHandlerAspect()+"+限流.";
  26. }
  27. }

测试一下:


  1. public class Test {
  2. public static void main(String[] args) {
  3. IAspect aspect = new LimitAspect(new ParamXssAspect(new LoggerAspect(new AspectImpl())));
  4. System.out.println(aspect.doHandlerAspect());
  5. }
  6. }

运行结果:

------

裸跑代码.+记录日志.+过滤危险字符.+限流.

------

通过上面可以看出,装饰者模式可以任意次序组装功能,是不是很灵活?另外,也可以把上述三个功能封装成注解@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也一样)的装饰者模式。


  1. public class ServletRequestWrapper implements ServletRequest {
  2. private ServletRequest request;//组合抽象接口到自己的类中
  3. public ServletRequestWrapper(ServletRequest request) {//可以构造自己
  4. if(request == null) {
  5. throw new IllegalArgumentException("Request cannot be null");
  6. } else {
  7. this.request = request;
  8. }
  9. }
  10. public ServletRequest getRequest() {
  11. return this.request;
  12. }
  13. public void setRequest(ServletRequest request) {
  14. if(request == null) {
  15. throw new IllegalArgumentException("Request cannot be null");
  16. } else {
  17. this.request = request;
  18. }
  19. }
  20. //省略...
  21. }

3.3、Spring Cache中的TransactionAwareCacheDecorator的装饰者模式。

其实从类名就可以看出。


  1. public class TransactionAwareCacheDecorator implements Cache {
  2. private final Cache targetCache;//把Cache组合到自己类中
  3. public TransactionAwareCacheDecorator(Cache targetCache) {//通过Cache构造自己
  4. Assert.notNull(targetCache, "Target Cache must not be null");
  5. this.targetCache = targetCache;
  6. }
  7. public <T> T get(Object key, Class<T> type) {
  8. return this.targetCache.get(key, type);
  9. }
  10. public void put(final Object key, final Object value) {
  11. // 判断是否开启了事务
  12. if (TransactionSynchronizationManager.isSynchronizationActive()) {
  13. // 将操作注册到 afterCommit 阶段
  14. TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
  15. public void afterCommit() {
  16. TransactionAwareCacheDecorator.this.targetCache.put(key, value);
  17. }
  18. });
  19. } else {
  20. this.targetCache.put(key, value);
  21. }
  22. }
  23. // ...省略...
  24. }

3.4、Mybatis中的装饰者。

Cache为抽象构件类,PerpetualCache为具体构件类,decorators包下的类为装饰类,这里没有抽象装饰类。

参考:

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

《码农翻身》刘欣

原文链接:从源码角度理解Java设计模式——装饰者模式

从源码角度理解Java设计模式——装饰者模式的更多相关文章

  1. JAVA设计模式--装饰器模式

    装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

  2. 【设计模式】Java设计模式 - 装饰者模式

    Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...

  3. Java设计模式——装饰者模式

    JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...

  4. 【转】java comparator 升序、降序、倒序从源码角度理解

    原文链接:https://blog.csdn.net/u013066244/article/details/78997869 环境jdk:1.7+ 前言之前我写过关于comparator的理解,但是都 ...

  5. 从源码角度学习Java动态代理

    前言 最近,看了一下关于RMI(Remote Method Invocation)相关的知识,遇到了一个动态代理的问题,然后就决定探究一下动态代理. 这里先科普一下RMI. RMI 像我们平时写的程序 ...

  6. 从源码角度理解android动画Interpolator类的使用

    做过android动画的人对Interpolator应该不会陌生,这个类主要是用来控制android动画的执行速率,一般情况下,如果我们不设置,动画都不是匀速执行的,系统默认是先加速后减速这样一种动画 ...

  7. 从JDK源码角度看java并发的公平性

    JAVA为简化开发者开发提供了很多并发的工具,包括各种同步器,有了JDK我们只要学会简单使用类API即可.但这并不意味着不需要探索其具体的实现机制,本文从JDK源码角度简单讲讲并发时线程竞争的公平性. ...

  8. 从JDK源码角度看java并发的原子性如何保证

    JDK源码中,在研究AQS框架时,会发现很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面 ...

  9. Java设计模式の装饰者模式

    目录 一.问题引入 二.设计原则 三.用装饰者模式解决问题 四.装饰者模式的特点 五.装饰者模式的定义 六.装饰者模式的实现 七.java.io包内的装饰者模式 一.问题引入 咖啡店的类设计: 一个饮 ...

随机推荐

  1. 给你的Swagger文档换套附魔皮肤吧

    前言 相信无论是前端或是后端的程序员对Swagger都不怎么陌生,没有用过应该也听说过 Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务. 简 ...

  2. 从零开始的SpringBoot项目 ( 七 ) 统一返回结果集Result 和 异常处理

    import java.io.Serializable; import lombok.Data; import org.springframework.http.HttpStatus; @Data p ...

  3. Python淘宝商品比价定向爬虫

    1.项目基本信息 目标: 获取淘宝搜索页面的信息,提取其中的商品名称和价格理解: 淘宝的搜索接口.翻页的处理 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道 ...

  4. Android开发之dp转像素,像素转换为dp工具类,详细代码,带有源文件下载地址。

    import android.content.Context; /** * @author 官网:http://blog.csdn.net/qq_21376985 * * David编写: 微博:ht ...

  5. Kubernetes-14:一文详解Pod、Node调度规则(亲和性、污点、容忍、固定节点)

    Kubernetes Pod调度说明 简介 Scheduler 是 Kubernetes 的调度器,主要任务是把定义的Pod分配到集群的节点上,听起来非常简单,但要考虑需要方面的问题: 公平:如何保证 ...

  6. 记一次内存飙升的Windbg

    背景 突然间接到运维的报警,我们一个服务,内存找过了6GB的占用.才6GB 也不是很大,因为在处理别的事情,服务dump一下暂时一放,然后半小时之后,接到了运维的Kafka堆积报警.然后切换着重启了一 ...

  7. 【小白学PyTorch】6 模型的构建访问遍历存储(附代码)

    文章转载自微信公众号:机器学习炼丹术.欢迎大家关注,这是我的学习分享公众号,100+原创干货. 文章目录: 目录 1 模型构建函数 1.1 add_module 1.2 ModuleList 1.3 ...

  8. vue 在使用数组的时候,数组内部数据发生变化,视图却没有改变

    data(){ return{ todos: [ {name: 'aa', age: 22}, {name: 'bb', age: 23} ] } } methods:{ changeTodos(){ ...

  9. 【原创】经验分享:一个Content-Length引发的血案(almost....)

    前言 上周在工作中遇到一个问题,挺有意思,这里记录一下.上周在工作中遇到一个问题,挺有意思,这里记录一下.标题起的很唬人,这个问题差点引发血案,花哥还是很严谨的一个人,后面备注了almost.... ...

  10. OneDrive Weblist

    OneIndex-Serverless 教程:https://zhuanlan.zhihu.com/p/74538287 https://github.com/LiuChangFreeman/OneI ...