详解Java如何优雅的使用装饰器模式

目录
  • 什么是装饰器模式
  • 优点
  • 缺点
  • 使用场景
  • 装饰器模式和代理模式的区别
  • 装饰器的简单实现
  • 装饰器模式实战
  • 小结

什么是装饰器模式

装饰器模式(Decorator Pattern): 在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责;

感觉和继承如出一辙,不改变父类,子类可拓展功能;

优点

装饰类和被装饰类可以独立发展,不会相互耦合

相比于继承,更加的轻便、灵活

可以动态扩展一个实现类的功能,不必修改原本代码

缺点

会产生很多的装饰类,增加了系统的复杂性。

这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

使用场景

对已有的目标功能存在不足,需要增强时,扩展类的功能。

动态增加功能,动态撤销

装饰器模式和代理模式的区别

代理是全权代理,目标根本不对外,全部由代理类来完成;装饰是增强,是辅助,目标仍然可以自行对外提供服务,装饰器只起增强作用。

装饰器模式强调的是:增强、新增行为;代理模式强调的是:对代理的对象施加控制,但不对对象本身的功能进行增强

装饰器模式:生效的对象还是原本的对象;代理模式:生效的是新的对象(代理对象)

装饰器和代理的区别

装饰器的简单实现

场景:天气太热了,喝点儿冰水解解暑;加点儿柠檬片,让果汁好喝点儿

先定义一个喝水的接口

public interface Drink {
    /**
     * 喝水
     */
    void drink();
}

写一个接口的实现

public class DrinkWater implements Drink {

    @Override
    public void drink() {
        System.out.println("喝水");
    }

}

一个简单的装饰器

public class DrinkDecorator implements Drink {

    private final Drink drink;

    public DrinkDecorator(Drink drink) {
        this.drink = drink;
    }

    @Override
    public void drink() {
        System.out.println("先加点儿柠檬片");
        drink.drink();
    }

}

开始测试

public class DrinkMain {
    public static void main(String[] args) {
        Drink drink = new DrinkWater();
        drink = new DrinkDecorator(drink);
        drink.drink();
    }
}

运行结果

先加点儿柠檬片
喝水

一个简单的装饰器模式例子就写完了;当然这种例子在实际项目中肯定是用不到的,这里只是先了解一下装饰器模式

装饰器模式实战

场景: 项目一期开发的时候,并没有给鉴权部分设置缓存;二期开发考虑到性能问题,想要给鉴权部分加上缓存,这里就选择了使用装饰器模式进行处理;

这里使用的缓存是spring的 spring-cache,不了解没关系,知道几个注解什么意思就行

@Cacheable 表示要对方法返回值进行缓存

@CacheEvict 删除缓存注解

为了简洁,以下代码均为伪代码

首先,需要一个权限的接口和实现类

public interface IDataAccessor {
    /**
     * 根据部门上级 id 获取所有子集部门
     */
    Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds);

    /**
     * 获取数据范围内的部门
     */
    Set<Long> deptFindScopeById(Long userId);

实现类(注意这里加了@Service, 交给spring处理)

@Service
public class ScopeDataAccessorImpl implements IDataAccessor {
    @Autowired
    private IDepartmentService departmentService;
    
    @Autowired
    private INodeScopeService nodeScopeService;

    @Override
    public Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds) {
        Set<Long> result = new HashSet<>();
        departmentService.departmentChildren(parentIds, result);
        return result;
    }
    
    @Override
    public Set<Long> deptFindScopeById(Long userId) {
        return nodeScopeService.deptFindScopeById(userId);
    }
}

接下来就是对之前的代码进行装饰,定义一个装饰器的实现类

(这个类没有 @Component, 没有直接交给spring管理;加了注解会报错:找到了2个bean)

public class DataAccessorDecorator implements IDataAccessor {
    private final IDataAccessor iDataAccessor;

    public DataAccessorDecorator(IDataAccessor iDataAccessor) {
        this.iDataAccessor = iDataAccessor;
    }

    @Cacheable(cacheNames = "dept:parentId", key = "#p0", sync = true)
    @Override
    public Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds) {
        return iDataAccessor.deptFindAllChildrenByParentIds(parentIds);
    }

    @Cacheable(cacheNames = "dept:scope:userId", key = "#p0", sync = true)
    @Override
    public Set<Long> deptFindScopeById(Long userId) {
        return iDataAccessor.deptFindScopeById(nodeId,userId);
    }
}

接下来还需要将这个装饰器的类注册到spring中

@Configuration
@ConditionalOnBean({IDataAccessor.class})
public class Config {
    
    @Bean
    @ConditionalOnBean({IDataAccessor.class})
    public DataAccessorDecorator dataAccessorDecorator(IDataAccessor iDataAccessor) {
        return new DataAccessorDecorator(iDataAccessor);
    }
}

根据业务,维护缓存更新;这里使用的监听部门和员工的变更事件

@Component
public class DataScopeEvict {

    /**
     * 清空部门相关缓存
     */
    @CacheEvict(cacheNames = {"dept:parentId"}, allEntries = true)
    public void department() {
    }

    /**
     * 清空用户相关缓存
     */
    @CacheEvict(cacheNames = {"dept:scope:userId"}, allEntries = true)
    public void user() {
    }
}
@Component
public class ScopeDataEventListener {
    @Autowired
    private DataScopeEvict evict;
    
 /**
     * 监听部门变更事件
     */
    @EventListener
    public void departmentEvent(DepartmentChangeEvent event) {
        // 1 增加 2 删除 3 上级部门变更
        evict.department();
    }

    /**
     * 监听user变更事件
     */
    @EventListener
    public void userEvent(UserChangeEvent event) {
        // 2 删除 3 主部门变更
        if (event.getType().equals(2) || event.getType().equals(3)) {
            evict.user();
        }
    }
}

一切准备就绪,使用的时候直接使用装饰器类就好了

@Service
public class UserService {
    
    @Autowired
    DataAccessorDecorator scopeDataAccessor;

    
    public Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds) {
        return scopeDataAccessor.deptFindAllChildrenByParentIds(parentIds);
    }
    
    
    public Set<Long> deptFindScopeById(Long userId) {
        return scopeDataAccessor.deptFindScopeById(userId);
    }
    
}

以上就是一个将装饰器模式应用到实际项目的例子;

在这个例子中,使用装饰器模式增强了原本的代码,不修改原本的代码,原本的代码也能正确提供服务,只不过没有使用缓存;只要方法名命名一致,只需修改注入的字段就可以升级完成,升级成本还是很低的。

这波使用装饰器模式加缓存的操作写到项目中,直接让你的代码 B ge pull full

小结

虽然使用装饰器模式看起来B格高,但还是要注意自己项目的场景,选择适合的方式解决问题。

以上就是详解Java如何优雅的使用装饰器模式的详细内容,更多关于Java装饰器模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • java设计模式之装饰器模式(Decorator)

    概述 装饰模式是对客户端以透明的方式扩展对象的功能,是继承关系的一个替代方案.也就是说,客户端并不会觉得对象在装饰前和装饰后有什么不同,装饰模式可以在不用创造更多子类的情况下,将对象的功能加以扩展,装饰模式的关键在于这种扩展是完全透明的. 模式的结构 UML类图: 装饰模式中的类角色: 抽象构件角色(Project):给出一个接口,以规范准备接收附加责任的对象 具体构件角色(Employe):定义一个将要接收附加责任的类 装饰角色(Manager):持有一个构件对象的实例,并定义一个与抽象构件接

  • java实现装饰器模式(Decorator Pattern)

    一.什么是装饰器模式   装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装.   这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能.   在不想增加更多子类的情况下扩展类,动态地给一个对象添加一些额外的职责.就增加功能来说,装饰器模式相比生成子类更为灵活. 二.装饰器模式的具体实现 1.结构图 2.分析 装饰器模式由组件和装饰者组成. 抽

  • 浅谈Java设计模式系列-装饰器模式

    一.概述 装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能. 不同于适配器模式和桥接模式,装饰器模式涉及的是单方,和代理模式相同,而且目标必须是抽象的. 而实际上,装饰器模式和代理模式的实现方式基本一致,只在目标的存在上有些差别,这个后面我们具体讲述. 二.初步分析 上面提到了两点: 涉及的是单方 目标是抽象的 我们来想一下,所谓单方主要指的是在整个装饰器模式中不存在双方调用,要解决的也不是双方调用的问题,而是解决单方提供对外服务的问题,这个单方在自行对外提供服务时,功能不足,或

  • Java中常用的设计模式之装饰器模式详解

    目录 优点 缺点 使用场景 一.实现方式 二.测试 总结 优点 1.装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能. 缺点 2.多层装饰比较复杂. 使用场景 1.扩展一个类的功能. 2.动态增加功能,动态撤销. 一.实现方式 假设一个场景,我们房间每天起床都要刷牙, 睡觉也要刷牙,刷牙的动作就是一个装饰器的作用,这样更利于我们的口腔健康.接下来我们就看看具体的装饰器如何实现. 1.每天生活的接口 package com.asurpl

  • Java设计模式以虹猫蓝兔的故事讲解装饰器模式

    目录 什么是装饰器模式 优点 缺点 知识点 装饰器模式实现 七侠 虹猫 加料 加盐 加孜然 测试 总结 模式: 装饰器模式 案例: 黑小虎抓住了七侠,把虹猫烤了 什么是装饰器模式 装饰器(Decorator)模式的定义: 指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式. 优点 1.装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用 2.通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效

  • 详解Java如何优雅的使用装饰器模式

    目录 什么是装饰器模式 优点 缺点 使用场景 装饰器模式和代理模式的区别 装饰器的简单实现 装饰器模式实战 小结 什么是装饰器模式 装饰器模式(Decorator Pattern): 在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责: 感觉和继承如出一辙,不改变父类,子类可拓展功能: 优点 装饰类和被装饰类可以独立发展,不会相互耦合 相比于继承,更加的轻便.灵活 可以动态扩展一个实现类的功能,不必修改原本代码 缺点 会产生很多的装饰类,增加了系统的复杂性. 这种比继承更加灵活机动的特

  • 详解KOA2如何手写中间件(装饰器模式)

    前言 Koa 2.x 版本是当下最流行的 NodeJS 框架, Koa 2.0 的源码特别精简,不像 Express 封装的功能那么多,所以大部分的功能都是由 Koa 开发团队(同 Express 是一家出品)和社区贡献者针对 Koa 对 NodeJS 的封装特性实现的中间件来提供的,用法非常简单,就是引入中间件,并调用 Koa 的 use 方法使用在对应的位置,这样就可以通过在内部操作 ctx 实现一些功能,我们接下来就讨论常用中间件的实现原理以及我们应该如何开发一个 Koa 中间件供自己和别

  • 详解Java后端优雅验证参数合法性

    1.首先创建一个测试实体类Person,并携带如上注解,其注解的作用描述在message package com.clickpaas.pojo; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Range; impo

  • 详解Java如何优雅地书写if-else

    目录 0. 引言 1. switch 2. 函数式接口 3. 策略模式 4. 卫语句 0. 引言 在日常开发中我们常常遇到有多个if else的情况,之间书写显得代码冗余难看,对于追求更高质量代码的同学,就会思考如何优雅地处理这种代码 下面我们来探讨下几种优化if else的方法 1. switch switch方法针对枚举值处理有不错的效果,比如针对不同的订单状态时要做不同的处理,因为状态值有限,这时我们就可以直接使用switch来针对不同状态做不同的处理: 原语句 public void b

  • 一文详解如何创建自己的Python装饰器

    目录 1.@staticmethod 2.自定义装饰器 3.带参数的装饰器 python装饰器在平常的python编程中用到的还是很多的,在本篇文章中我们先来介绍一下python中最常使用的@staticmethod装饰器的使用. 之后,我们会使用两种不同的方式来创建自己的自定义python装饰器以及如何在其他地方进行调用. 1.@staticmethod @staticmethod是python开发者经常用来在一个类中声明该函数是一个静态函数时使用到的装饰器,比如创建一个HelloWorld的

  • 详解如何在JavaScript中使用装饰器

    目录 安装 vite配置 webpack配置 使用 语法: @+函数名 类装饰器 带参数的修饰器 类成员装饰器 多个装饰器的执行顺序 应用 延迟 节流 防抖 Decorator装饰器是ES7的时候提案的特性,目前处于Stage 3候选阶段(2022年10月). 装饰器简单来说就是修改类和类方法的语法糖,很多面向对象语言都有装饰器这一特性. 为了使用装饰器特性,我们需要用进行babel转义.这里需要用到的是@babel/plugin-proposal-decorators. 安装 npm inst

  • Java结构性设计模式中的装饰器模式介绍使用

    目录 装饰器模式 概述 实现原理 主要角色 应用场景 优缺点 装饰器模式的基本使用 创建抽象组件 具体组件 抽象装饰器 具体装饰器 客户端调用 装饰器模式 概述 装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),属于结构型模式. 它是指在不改变原有对象的基础之上,允许向一个现有的对象添加新的功能,同时又不改变其结构,作为现有的类的一个包装. 这种模式创建一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能. 提供了比继承

  • 详解Java的设计模式编程中的原型模式

    定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 类型:创建类模式 类图: 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype.Prototype类需要具备以下两个条件: 实现Cloneable接口.在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法.在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedExcepti

  • 详解Java设计模式编程中的中介者模式

    定义:用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互. 类型:行为类模式 类图: 中介者模式的结构        中介者模式又称为调停者模式,从类图中看,共分为3部分:  抽象中介者:定义好同事类对象到中介者对象的接口,用于各个同事类之间的通信.一般包括一个或几个抽象的事件方法,并由子类去实现. 中介者实现类:从抽象中介者继承而来,实现抽象中介者中定义的事件方法.从一个同事类接收消息,然后通过消息影响其他同时类. 同事类

  • 详解Java线程池如何实现优雅退出

    目录 shutdown()方法 shutdownNow()方法 awaitTermination(long, TimeUnit)方法 在[高并发专题]中,我们从源码角度深度分析了线程池中那些重要的接口和抽象类.深度解析了线程池是如何创建的,ThreadPoolExecutor类有哪些属性和内部类,以及它们对线程池的重要作用.深度分析了线程池的整体核心流程,以及如何拆解Worker线程的执行代码,深度解析Worker线程的执行流程. 本文,我们就来从源码角度深度解析线程池是如何优雅的退出程序的.首

随机推荐