目录
什么是装饰器模式
装饰器模式(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装饰器模式的资料请关注其它相关文章!
:本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可, 转载请附上原文出处链接。
1、本站提供的源码不保证资源的完整性以及安全性,不附带任何技术服务!
2、本站提供的模板、软件工具等其他资源,均不包含技术服务,请大家谅解!
3、本站提供的资源仅供下载者参考学习,请勿用于任何商业用途,请24小时内删除!
4、如需商用,请购买正版,由于未及时购买正版发生的侵权行为,与本站无关。
5、本站部分资源存放于百度网盘或其他网盘中,请提前注册好百度网盘账号,下载安装百度网盘客户端或其他网盘客户端进行下载;
6、本站部分资源文件是经压缩后的,请下载后安装解压软件,推荐使用WinRAR和7-Zip解压软件。
7、如果本站提供的资源侵犯到了您的权益,请邮件联系: 442469558@qq.com 进行处理!
猪小侠源码-最新源码下载平台 PHP教程 详解Java如何优雅的使用装饰器模式 http://www.20zxx.cn/586310/xuexijiaocheng/qes.html
猪小侠源码,优质资源分享网
相关文章
- java非法字符‘\\ufeff‘解决方法 2024-03-11
- Java中单体应用锁的局限性&分布式锁 2024-03-11
- 如何通过php函数解决页面渲染慢的问题? 2024-03-11
- 如何评估php性能优化函数的效果? 2024-03-11
- 如何利用PHP脚本在Linux中进行目录操作 2024-03-11
- 如何通过PHP脚本在Linux中进行系统监测 2024-03-11
- 如何使用php函数来优化表单处理和提交功能? 2024-03-11
- 如何通过PHP脚本在Linux服务器上实现数据加密 2024-03-11
- 如何通过php函数来优化验证码生成和校验? 2024-03-11
- 如何使用php函数来优化多语言支持功能? 2024-03-11
做猪小侠源码的代理,提供一站式服务
如果你不懂得搭建网站或者服务器,小程序,源码之类的怎么办? 第一通过本站学习各种互联网的技术 第二就是联系客服,我帮帮你搭建(当然要收取部分的费用) 第三成为我们的代理,我们提供整套的服务。