如何巧妙运用Java装饰器模式实现优雅扩展?

2026-05-24 02:401阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1767个文字,预计阅读时间需要8分钟。

如何巧妙运用Java装饰器模式实现优雅扩展?

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

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

什么是装饰器模式

装饰器模式(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();         }     } }

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

如何巧妙运用Java装饰器模式实现优雅扩展?

@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装饰器模式的资料请关注自由互联其它相关文章!

本文共计1767个文字,预计阅读时间需要8分钟。

如何巧妙运用Java装饰器模式实现优雅扩展?

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

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

什么是装饰器模式

装饰器模式(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();         }     } }

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

如何巧妙运用Java装饰器模式实现优雅扩展?

@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装饰器模式的资料请关注自由互联其它相关文章!