文档章节

设计模式--装饰者模式

gaob2001
 gaob2001
发布于 06/19 06:07
字数 1235
阅读 9
收藏 0
点赞 0
评论 0

装饰者模式

定义

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

通用类图

意图

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

优点

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

缺点

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

应用场景

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

扩展

说明

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

实践

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

1.Controller

@RestController
public class HotelController {

    @Resource
    private 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 {

    @Resource
    private HotelDao hotelDao;

    @Override
    public 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 调用即可。

© 著作权归作者所有

gaob2001
粉丝 3
博文 47
码字总数 29688
作品 0
黄浦
程序员
JavaScript常用设计模式

设计模式 设计模式是一种在长时间的经验与错误中总结出来可服用的解决方案。 设计模式主要分为3类: 创建型设计模式:专注于处理对象的创建 Constructor构造器模式,Factory工厂模式,Singl...

a独家记忆
07/13
0
0
JavaScript 中常见设计模式整理

开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式。本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知。 JavaScript 中...

牧云云
05/18
0
0
我的Java设计模式-代理模式

写完上一篇之后有小伙伴问我有没有写过代理模式,想看看我的理解。原本我的设计模式系列是按照创建型-行为型-结构型的顺序写下去的,既然小伙伴诚心诚意了,我就大发慈悲的穿插一篇代理模式。...

Jet啟思
2017/11/29
0
0
Java 设计模式(14) —— 复合模式

一、复合模式 模式常一起使用,组合在一个设计解决方案中 复合模式在一个解决方案中结合两个或多个模式,能解决一般性或一系列的问题 二、示例 本次设计模式讲解中无代码示例,由于复合模式是...

磊_lei
05/26
0
0
Android 设计模式-装饰模式(Decorator Pattern)

定义 饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 UML结构图 Component:组件对象接口 Concre...

Code猎人
05/10
0
0
ES7 Decorator 装饰者模式

原作者:玄农 装饰模式 设计模式大家都有了解,网上有很多系列教程,比如 JS设计模式等等。 这里只分享 装饰者模式 以及在 如何使用 ES7 的 概念 装饰模式 v.s. 适配器模式 装饰模式和适配器...

_朴灵_
05/14
0
0
设计模式之修饰器模式详解(附源代码)

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

thorhill
03/24
0
0
python 与设计模式 ——工厂与装饰者

python 与设计模式第二篇 添加了test.py,里面的单元测试有使用的方法。 源码地址:[http://git.oschina.net/duoduo3_69/python_design_pattern][1] git checkout v002(这个版本与此篇博客相符...

duoduo3_69
2013/11/27
0
1
使用合适的设计模式一步步优化前端代码

作者:晓飞 本文原创,转载请注明作者及出处 --- 在后端语言中,设计模式应用的较为广泛。如Spring中常见的工厂模式、装饰者模式、单例模式、迭代器模式。但是在日常的前端开发中,设计模式使...

iKcamp
2017/10/27
0
0
Android 装饰者模式初探

前提 最近刚换了一份工作,这段时间一直在熟悉公司的代码逻辑,从中受益颇多。里面的设计模式也挺多的,运用的那个潇洒飘逸,让我好生羡慕。自己对设计模式这块理解的不是特别的深入,能拿出...

Silence潇湘夜雨
07/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

SpringBoot | 第十章:Swagger2的集成和使用

前言 前一章节介绍了mybatisPlus的集成和简单使用,本章节开始接着上一章节的用户表,进行Swagger2的集成。现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的...

oKong
今天
2
0
Python 最小二乘法 拟合 二次曲线

Python 二次拟合 随机生成数据,并且加上噪声干扰 构造需要拟合的函数形式,使用最小二乘法进行拟合 输出拟合后的参数 将拟合后的函数与原始数据绘图后进行对比 import numpy as npimport...

阿豪boy
今天
1
0
云拿 无人便利店

附近(上海市-航南路)开了家无人便利店.特意进去体验了一下.下面把自己看到的跟大家分享下. 经得现场工作人员同意后拍了几张照片.从外面看是这样.店门口的指导里强调:不要一次扫码多个人进入....

周翔
昨天
1
0
Java设计模式学习之工厂模式

在Java(或者叫做面向对象语言)的世界中,工厂模式被广泛应用于项目中,也许你并没有听说过,不过也许你已经在使用了。 简单来说,工厂模式的出现源于增加程序序的可扩展性,降低耦合度。之...

路小磊
昨天
161
1
npm profile 新功能介绍

转载地址 npm profile 新功能介绍 npm新版本新推来一个功能,npm profile,这个可以更改自己简介信息的命令,以后可以不用去登录网站来修改自己的简介了 具体的这个功能的支持大概是在6这个版...

durban
昨天
1
0
Serial2Ethernet Bi-redirection

Serial Tool Serial Tool is a utility for developing serial communications, custom protocols or device testing. You can set up bytes to send accordingly to your protocol and save......

zungyiu
昨天
1
0
python里求解物理学上的双弹簧质能系统

物理的模型如下: 在这个系统里有两个物体,它们的质量分别是m1和m2,被两个弹簧连接在一起,伸缩系统为k1和k2,左端固定。假定没有外力时,两个弹簧的长度为L1和L2。 由于两物体有重力,那么...

wangxuwei
昨天
0
0
apolloxlua 介绍

##项目介绍 apolloxlua 目前支持javascript到lua的翻译。可以在openresty和luajit里使用。这个工具分为两种模式, 一种是web模式,可以通过网页使用。另外一种是tool模式, 通常作为大规模翻...

钟元OSS
昨天
2
0
Mybatis入门

简介: 定义:Mybatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。 途径:MyBatis通过XML文件或者注解的形式配置映射,实现数据库查询。 特性:动态SQL语句。 文件结构:Mybat...

霍淇滨
昨天
2
0
开发技术瓶颈期,如何突破

前言 读书、学习的那些事情,以前我也陆续叨叨了不少,但总觉得 “学习方法” 就是一个永远在路上的话题。个人的能力、经验积累与习惯方法不尽相同,而且一篇文章甚至一本书都很难将学习方法...

_小迷糊
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部