0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

科技绿洲 来源:Java技术指北 作者:Java技术指北 2023-10-08 14:25 次阅读

什么是装饰器模式

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

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

优点

  1. 装饰类和被装饰类可以独立发展,不会相互耦合
  2. 相比于继承,更加的轻便、灵活
  3. 可以动态扩展一个实现类的功能,不必修改原本代码

缺点

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

使用场景

  1. 对已有的目标功能存在不足,需要增强时,扩展类的功能。
  2. 动态增加功能,动态撤销

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

  • 代理是全权代理,目标根本不对外,全部由代理类来完成;装饰是增强,是辅助,目标仍然可以自行对外提供服务,装饰器只起增强作用。
  • 装饰器模式强调的是: 增强、新增行为 ;代理模式强调的是: 对代理的对象施加控制,但不对对象本身的功能进行增强
  • 装饰器模式:生效的对象还是原本的对象;代理模式:生效的是新的对象(代理对象)

图片
装饰器和代理的区别

装饰器的简单实现

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

先定义一个喝水的接口

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格高,但还是要注意自己项目的场景,选择适合的方式解决问题。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8563

    浏览量

    150991
  • 缓存
    +关注

    关注

    1

    文章

    239

    浏览量

    26665
  • 代码
    +关注

    关注

    30

    文章

    4774

    浏览量

    68503
收藏 人收藏

    评论

    相关推荐

    适配器模式代理模式区别

      代理模式  组成:  抽象角色:通过接口或抽象类声明真实角色实现的业务方法。  代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的
    发表于 10-22 15:17

    MCU模式和RGB模式区别

    模式(也写成MPU模式的)。只有TFT模块才有RGB接口。但应用比较多的就是MCU模式和RGB模式区别有以下几点:1.MCU接口:会解码命
    发表于 11-03 08:53

    适配器模式装饰模式代理模式区别

    适配器模式装饰模式代理模式都属于设计模式中的结
    发表于 10-18 15:53 1.7w次阅读
    适配器<b class='flag-5'>模式</b>、<b class='flag-5'>装饰</b><b class='flag-5'>器</b><b class='flag-5'>模式</b>、<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的<b class='flag-5'>区别</b>

    适配器模式代理模式区别

    适配器模式:适配器模式有时候也称包装样式或者包装。将一个类的接口转接成用户所期待的。代理模式:为其他对象提供一种代理以控制对这个对象的访问。
    发表于 01-12 11:56 5273次阅读
    适配器<b class='flag-5'>模式</b>和<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的<b class='flag-5'>区别</b>

    适配器模式装饰模式区别

    装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。在计算机编程中,适配器模式(有时候也称包装样式或者包
    发表于 01-15 10:31 6998次阅读
    适配器<b class='flag-5'>模式</b>和<b class='flag-5'>装饰</b><b class='flag-5'>模式</b>的<b class='flag-5'>区别</b>

    西门子PLC的等时模式和非等时模式区别

    西门子PLC的等时模式和非等时模式区别说明。
    发表于 04-23 15:58 3次下载

    LCD MCU模式和RGB模式

    应用比较多的就是MUC模式和RGB模式区别有以下几点:1.MCU接口:会解码命令,由timing generator产生时序信号,驱动COM和SEG驱。RGB接口:在写LCD re
    发表于 10-28 09:50 19次下载
    LCD MCU<b class='flag-5'>模式</b>和RGB<b class='flag-5'>模式</b>

    GoF给装饰模式的定义

    的源码,就会发现 middleware 功能的实现用的就是装饰模式(Decorator Pattern)。
    的头像 发表于 06-29 10:22 804次阅读

    GoF设计模式代理模式

    它是一个使用率非常高的设计模式,在现实生活中,也是很常见。比如,演唱会门票黄牛。假设你需要看一场演唱会,但官网上门票已经售罄,于是就当天到现场通过黄牛高价买了一张。在这个例子中,黄牛就相当于演唱会门票的代理,在正式渠道无法购买门票的情况下,你通过
    的头像 发表于 10-17 09:45 926次阅读

    嵌入式C语言软件设计之装饰模式(Decorator Pattern)

    装饰模式(Decorator Pattern),是结构型设计模式的一种,装饰
    发表于 03-01 10:55 407次阅读

    演示装饰模式的用法

    装饰模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是
    的头像 发表于 06-08 11:16 564次阅读
    演示<b class='flag-5'>装饰</b><b class='flag-5'>器</b><b class='flag-5'>模式</b>的用法

    设计模式结构性:代理模式

    代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式
    的头像 发表于 06-09 15:27 832次阅读
    设计<b class='flag-5'>模式</b>结构性:<b class='flag-5'>代理</b><b class='flag-5'>模式</b>

    设计模式代理模式的使用场景

    设计模式在我看来更像是一种设计思维或设计思想,它就像《孙子兵法》一样,为你的项目工程提供方向,让你的项目工程更加健壮、灵活,延续生命力。本文即将分享的是设计模式的其中一种:代理模式
    的头像 发表于 10-08 14:34 1019次阅读
    设计<b class='flag-5'>模式</b>中<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的使用场景

    示波器滚动模式与标准模式区别

    示波器滚动模式与标准模式区别  示波器是一种电子测试仪器,它用于显示电压随时间变化的波形图。示波器可以设置为两种显示模式:滚动模式和标准
    的头像 发表于 11-07 10:13 2178次阅读

    网络桥接模式是什么? 网络桥接模式和路由模式区别

    网络桥接模式是一种网络连接方式,它可以将多个设备连接在一起,使它们可以相互通信。在网络桥接模式下,每个设备都可以直接与其他设备通信,而不需要经过路由或其他中间设备。这种连接方式通常用于局域网中
    的头像 发表于 05-10 13:48 4239次阅读