当前位置: 首页 > 科技观察

如何优雅的使用装饰者模式

时间:2023-03-17 19:21:15 科技观察

大家好,我是北君。装饰器设计模式大家肯定都听说过,但是你用过吗?今天和大家分享一下装饰者模式的使用方法。什么是装饰者模式?装饰者模式:在不改变对象本身的情况下,在程序运行过程中动态地为对象添加职责;感觉和继承完全一样,不改变父类,子类可以扩展功能;优点装饰类和被装饰类可以独立开发,相互之间不需要耦合;与继承相比,它更便携、更灵活;实现类的功能可以在不修改原有代码的情况下动态扩展;缺点是会生成很多装饰类,增加系统复杂度。这个比继承更灵活的特性,也意味着装饰模式比继承更容易出错,排错也非常困难。对于多次装饰的对象,在调试的时候可能需要一步一步的检查才能发现错误,比较繁琐。使用场景现有目标功能不足,需要增强时,扩展类的功能。装饰器模式和代理模式动态添加功能和动态撤销的区别在于,代理是全能代理,目标完全不在外部,全部由代理类完成;decoration是增强和辅助,target仍然可以自己对外提供服务,decorator只是起到Reinforcement的作用。装饰者模式强调:增强和新行为;代理模式强调:对代理对象施加控制,但不增强对象本身的功能。装饰者模式:生效的对象仍然是原来的对象;代理模式:新对象(代理对象)有效。装饰器和代理的区别装饰器的简单实现:天气太热,喝点冰水解暑;加入一些柠檬片,使果汁的味道更好。首先定义一个饮用水的接口;publicinterfaceDrink{/***drinkwater*/voiddrink();}写一个接口实现;publicclassDrinkWaterimplementsDrink{@Overridepublicvoiddrink(){System.out.println("喝饮料");}}一个简单的装饰器;publicclassDrinkDecoratorimplementsDrink{privatefinalDrink饮料;publicDrinkDecorator(Drinkdrink){this.drink=drink;}@Overridepublicvoiddrink(){System.out.println("先加些柠檬片");喝.喝();}}开始测试;公共类DrinkMain{publicstaticvoidmain(String[]args){Drinkdrink=newDrinkWater();drink=newDrinkDecorator(饮料);喝.喝();}}运行结果;先加点柠檬片喝水,一个简单的装饰器模式例子就完成了;当然这种例子在实际项目中是肯定用不到的,这里只是看一下装饰器模式装饰器模式的实战场景:在项目开发的第一阶段,没有为鉴权设置缓存部分;在二期开发中,考虑到性能问题,想在认证部分加入缓存,这里选择使用装饰器模式进行处理;这里使用的缓存是spring的spring-cache,看不懂也没关系,看几个注解就知道什么意思;@Cacheable表示缓存方法的返回值;@CacheEvict删除缓存注解;对于Concise,以下代码均为伪代码首先,需要一个有权限的接口和实现类;publicinterfaceIDataAccessor{/***根据部门Levelid获取所有子部门*/SetdeptFindAllChildrenByParentIds(CollectionparentIds);/***获取数据范围内的部门*/SetdeptFindScopeById(LonguserId);实现类(注意这里加@Service,交给spring);@ServicepublicclassScopeDataAccessorImplimplementsIDataAccessor{@AutowiredprivateIDepartmentServicedepartmentService;@AutowiredprivateINodeScopeServicenodeScopeService;@OverridepublicSetdeptFindAllChildrenByParentIds(CollectionparentIds){Setresult=newHashSet<>();departmentService.departmentChildren(parentIds,result);返回结果;}@OverridepublicSetdeptFindScopeById(LonguserId){返回nodeScopeService.deptFindScopeById(userId);装饰代码,定义装饰器实现类;(该类没有@Component,不直接交给spring管理;添加注解会报错:2beanswerefound);公共类DataAccessorDecorator实现IDataAccessor{私有最终IDataAccessoriDataAccessor;publicDataAccessorDecorator(IDataAccessoriDataAccessor){this.iDataAccessor=iDataAccessor;}@Cacheable(cacheNames="dept:parentId",key="#p0",sync=true)@OverridepublicSetdeptFindAllChildrenByParentIds(CollectionparentIds){returniDataAccessor.deptFindAllChildrenByParentIds(parentIds);}@Cacheable(cacheNames="dept:scope:userId",key="#p0",sync=true)@OverridepublicSetdeptFindScopeById(LonguserId){returniDataAccessor.deptFindScopeById(nodeId,userId);接下来需要在spring中注册这个装饰器的类;@Configuration@ConditionalOnBean({IDataAccessor.class})publicclassConfig{@Bean@ConditionalOnBean({IDataAccessor.class})publicDataAccessorDecoratordataAccessorDecorator(IDataAccessoriDataAccessor){返回新的DataAccessorDecorator(iDataAccessor);}}根据业务,维护缓存更新;这里用来监听部门和员工的变化事件;@ComponentpublicclassDataScopeEvict{/***清除部门相关缓存*/@CacheEvict(cacheNames={"dept:parentId"},allEntries=true)publicvoiddepartment(){}/***清除用户相关缓存*/@CacheEvict(cacheNames={"dept:scope:userId"},allEntries=true)publicvoiduser(){}}@ComponentpublicclassScopeDataEventListener{@AutowiredprivateDataScopeEvictevict;/***监听部门变更事件*/@EventListenerpublicvoiddepartmentEvent(DepartmentChangeEventevent){//1add2delete3superdepartmentchangeevict.department();}/***监听用户变更事件*/@EventListenerpublicvoiduserEvent(UserChangeEventevent){//2delete3maindepartmentChangeif(event.getType().equals(2)||event.getType().equals(3)){evict.user();}}}万事俱备,使用的时候直接使用装饰器类就可以了;@ServicepublicclassUserService{@AutowiredDataAccessorDecoratorscopeDataAccessor;公共SetdeptFindAllChildrenByParentIDS(CollectionparentIds){returnscopeDataAccessor.deptFindAllChildrenByParentIds(parentIds);}publicSetdeptFindScopeById(LonguserId){returnscopeDataAccessor.deptFindScopeById(userId);}}以上是应用于实际项目示例的装饰器模式;本例中使用装饰器模式对原代码进行了增强,原代码无需修改原代码即可正确提供服务,但不使用缓存;只要方法名一致,只需要修改注入的字段即可升级,升级成本还是很低的。这一波使用装饰器模式加缓存的操作写到项目中,你的代码Bgepullfull直接made了。总结虽然使用装饰器模式看起来档次很高,但是还是要注意自己项目的场景,选择合适的方式来解决问题。