2019独角兽企业重金招聘Python工程师标准>>>

装饰者模式

定义

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

通用类图

意图

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

优点

  1. 装饰类和被装饰类可以独立发展,而不会相互耦合。
  2. 装饰模式是继承关系的一个替代方案。
  3. 装饰模式可以动态地扩展一个实现类的功能。

缺点

多层装饰容易导致问题,尽量减少装饰类的数量,以便降低系统的复杂度。

应用场景

  1. 需要扩展一个类的功能,或给一个类增加附加功能。
  2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  3. 需要为一批的兄弟类进行改装或加装功能。

扩展

说明

装饰模式是对继承的有力补充。要知道继承不是万能的,继承可以解决实际的问题,但是在项目中你要考虑诸如易维护、易扩展、易复用等,而且在一些情况下要是用继承就会增加很多子类,而且灵活性非常差,那当然维护也不容易,也就是说装饰模式可以替代继承,解决我们类膨胀的问题。同时,继承是静态地给类增加功能,而装饰模式则是动态地增加功能。

实践

下面结合spring项目对装饰者模式举一个简单的例子。

1.Controller

@RestController
public class HotelController {@Resourceprivate HotelManager hotelManager;@GetMapping("/listHotel")public String listHotel() {return JSON.toJSONString(hotelManager.listHotel());}}

有一个controller,接收一个 listHotel 的 get 请求,调用 hotelManager (service层)的 listHotel() 接口。

2.Service

public interface HotelManager {List<Hotel> listHotel();}@Service("hotelManager")
public class HotelManagerImpl implements HotelManager {@Resourceprivate HotelDao hotelDao;@Overridepublic List<Hotel> listHotel() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return hotelDao.listHotel();}
}

hotelManager 的 listHotel 接口直接调用 hotelDao 的 listHotel() 接口,从数据库中查询数据。这里 Thread.sleep(3000) 为了模拟从数据库查询数据返回比较慢的情况。

这是在java web应用开发中一个很简单的,从前端接收一个请求到数据库查询数据返回给前端的一个动作。当这个查询效率非常低,耗时非常多,但是数据又不会经常变的情况下,我们可以通过把数据放到缓存(redis memcached等)里面来提高查询效率。

如果在项目进度非常紧的情况下,我们很可能写出下面的代码

public List<Hotel> listHotel() {if (在redis中可以查询到结果) {return redis.get(结果)} else {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return hotelDao.listHotel();}}

首先非常丑,其次多余的代码和业务逻辑混在一起,让人不能一眼就看清这个接口做了什么事情。

接下来通过装饰者模式重构一下
增加一个自定义注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedisCache {
}

这是一个空的方法级别的注解,表示被该注解标示的方法返回的数据都应缓存在redis。

恢复 hotelManager.listHotel() 方法并且头上加入自定义注解

@RedisCache
@Override
public List<Hotel> listHotel() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return hotelDao.listHotel();
}

新建一个 HotelManagerDecorate 类

public class HotelManagerDecorate {private HotelManager hotelManager;private RedisTemplate redisTemplate;public List<Hotel> listHotel() throws Exception{Method method = Util.getTarget(hotelManager).getClass().getDeclaredMethod("listHotel", null);if (method.isAnnotationPresent(RedisCache.class)) {List<Hotel> redisHotelList = (List<Hotel>) redisTemplate.opsForList().range("a", 0, -1);return Optional.ofNullable(redisHotelList).filter(list -> list.size() > 0).orElseGet(() -> {List<Hotel> hotelList = hotelManager.listHotel();redisTemplate.opsForList().leftPushAll("a", hotelList);redisTemplate.expire("a", 300, TimeUnit.SECONDS);return hotelList;});} else {return hotelManager.listHotel();}}}

这是一个装饰者类,装饰了 hotelManager 这个类,通过反射获取到方法上面的注解,判断是否存在 RedisCache 这个注解,如果存在,则去redis中取得数据,否则从数据查出数据放入redis再返回。
Optional类是 java8 新增的类,有兴趣的可以了解一下。

修改 HotelController 为

@GetMapping("/listHotel")
public String listHotel() throws Exception{HotelManagerDecorate hotelManagerDecorate = new HotelManagerDecorate(hotelManager, redisTemplate);return JSON.toJSONString(hotelManagerDecorate.listHotel());
}

修改了调用方法,由原来调用hotelManager改为调用装饰者类,其实装饰者类最终还是调用了hotelManager.listHotel() 方法。

这么修改之后就可以发现,再没有修改原有代码的基础上,动态的给 hotelManager.listHotel() 方法增加了缓存功能,对方法的功能实现了增强,并且增强代码与原来的业务逻辑是分离的。装饰者只负责增强功能,业务代码根本不知道装饰者的存在,这样的做法非常易于扩展和维护,具体如何调用只是由 controller 层(高层)决定。如果将来想修改一下 RedisCache 逻辑,直接在装饰类中修改即可,根本不会影响到业务逻辑。如果将来想换一个增强的功能,直接新建一个装饰者类,修改一下 controller 调用即可。

转载于:https://my.oschina.net/u/232911/blog/1831790

设计模式--装饰者模式相关推荐

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

    Python设计模式-装饰器模式 代码基于3.5.2,代码如下; #coding:utf-8 #装饰器模式class Beverage():name = ""price = 0.0 ...

  2. [Head First设计模式]山西面馆中的设计模式——装饰者模式

    原文:[Head First设计模式]山西面馆中的设计模式--装饰者模式 引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里 ...

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

    本文是阅读 Head First 设计模式--装饰者模式的总结. 这本书的教学模式很不错,个人很喜欢,由实际的案例由浅入深,循序渐进的让你明白良好的设计是多么的优雅迷人(回头看看自己的代码,WTF!) ...

  4. 设计模式装饰者模式_装饰者模式如何拯救了我的一天

    设计模式装饰者模式 在工作中,我正在处理庞大的Java代码库,该代码库是由许多不同的开发人员在15年的时间里开发的. 并不是所有的事情都由书来完成,但是同时我通常无法重构遇到的每一个奇怪的事物. 尽管 ...

  5. 23种设计模式——装饰者模式

    文章目录 23种设计模式--装饰者模式 1.装饰者模式概述 2.装饰者模式的结构 3.装饰者模式的实现 4.装饰者模式的应用场景 23种设计模式--装饰者模式 1.装饰者模式概述 背景 有些人为了早上 ...

  6. 读书笔记---Head First 设计模式--- 装饰者模式

    读书笔记-Head First 设计模式- 装饰者模式 装饰者模式(Decorator Pattern) 装饰者模式--动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. ...

  7. Go 设计模式 - 装饰器模式

    装饰模式使用对象组合的方式动态改变或增加对象行为.Go语言借助于匿名组合和非入侵式接口可以很方便实现装饰模式.使用匿名组合,在装饰器中不必显式定义转调原对象方法. 设计模式 装饰器模式 装饰器模式主要 ...

  8. 10. 设计模式-装饰者模式

    文章目录 设计模式-装饰者模式 1. 案例引出装饰者模式 2. 装饰者模式 2.1 装饰者模式定义 2.2 装饰者模式原理 2.3 装饰者模式解决星巴克咖啡订单问题 2.4 代码实现 抽象类Drink ...

  9. Spring设计模式(装饰器模式)

    Spring设计模式(装饰器模式) 模式的定义: 装饰者模式定义: ​ 动态地为一个对象添加一些额外的职责,若要扩展一个对象的功能,装饰者提供了比继承更有弹性的替代方案. 模式的结构图 : 模式包含角 ...

  10. 设计模式 装饰者模式 带你重回传奇世界

    今天继续设计模式之旅,给大家带来装饰者模式,国际惯例,先看定义. 装饰者模式:若要扩展功能,装饰者提供了比集成更有弹性的替代方案,动态地将责任附加到对象上. 先简单描述下装饰者模式发挥作用的地方,当我 ...

最新文章

  1. ExtAspNet应用技巧(十九) - 日志管理
  2. python自媒体创作_做自媒体该做什么领域?
  3. 定位 - MapKit-自定义大头针
  4. 风变编程python助教_花30天时间,学完了风变编程Python基础语法课
  5. 两个Oracle JDK的故事
  6. Mr.J--心形跳动demo
  7. 违规停放共享单车 319人被纳入限制骑行“黑名单”
  8. 14-Scala之隐式转换
  9. 装机防骗武器——鲁大师
  10. c语言程序设计2020年版,2020年新版c语言程序设计题库.docx
  11. 如何在PPT中插入HTML页面|如何使用控件将Pyecharts图表插入PPT|ActiveX
  12. 八段数码管数字显示实验c语言,实验报告__实验七_八段数码管显示实验
  13. Axure8元件库.rplib(Iaxure)
  14. oracle同一个库不同用户,oracle同一个库上面,不同用户相互赋予权限
  15. uniapp小程序自定义分享按钮
  16. 四川理工计算机专业好不好,急!!!!四川理工 、 宜宾学院 、 内江师范学院 哪个学校计算机专业好点?...
  17. python实现推广小项目
  18. ES6 Promise 对象
  19. Linux日志怎么分析
  20. JQ手册 JQ方法大全 jq获取表单值与赋值代码 50个JQ的例子

热门文章

  1. 排序_简单排序_选择排序
  2. 热烈庆贺清明小长假的到来
  3. smarty模版中使用图片的路径问题
  4. 经济危机下企业倒闭的真相
  5. 初步学习“C#枚举”
  6. Average Score39届亚洲赛牡丹江站A题
  7. 【运筹学】指派问题、匈牙利法总结 ( 指派问题 | 克尼格定理 | 匈牙利法 | 行列出现 0 元素 | 试指派 | 打 √ | 直线覆盖 ) ★★★
  8. 【Flutter】Flutter 项目中使用 Flutter 插件 ( Flutter 插件管理平台 | 搜索 Flutter 插件 | 安装 Flutter 插件 | 使用 Flutter 插件 )
  9. 【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )
  10. 【Kotlin】Kotlin enum 枚举类 ( 常用用法 | 初始化成员变量 | 实现抽象方法 | 实现接口 | 获取名称和位置索引 | 调用枚举常量方法 )