欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > asp.net >内容正文

asp.net

设计模式--装饰者模式

发布时间:2025/6/16 asp.net 56 豆豆
生活随笔 收集整理的这篇文章主要介绍了 设计模式--装饰者模式 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

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

装饰者模式

定义

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

通用类图

意图

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

优点

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

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

    应用场景

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

    说明

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

    实践

    下面结合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

    总结

    以上是生活随笔为你收集整理的设计模式--装饰者模式的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。