0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

裝飾器模式和代理模式的區(qū)別

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-10-08 14:25 ? 次閱讀

什么是裝飾器模式

裝飾器模式(Decorator Pattern): 在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)象動(dòng)態(tài)的添加職責(zé);

感覺和繼承如出一轍,不改變父類,子類可拓展功能;

優(yōu)點(diǎn)

  1. 裝飾類和被裝飾類可以獨(dú)立發(fā)展,不會(huì)相互耦合
  2. 相比于繼承,更加的輕便、靈活
  3. 可以動(dòng)態(tài)擴(kuò)展一個(gè)實(shí)現(xiàn)類的功能,不必修改原本代碼

缺點(diǎn)

  1. 會(huì)產(chǎn)生很多的裝飾類,增加了系統(tǒng)的復(fù)雜性。
  2. 這種比繼承更加靈活機(jī)動(dòng)的特性,也同時(shí)意味著裝飾模式比繼承易于出錯(cuò),排錯(cuò)也很困難,對(duì)于多次裝飾的對(duì)象,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級(jí)排查,較為繁瑣。

使用場(chǎng)景

  1. 對(duì)已有的目標(biāo)功能存在不足,需要增強(qiáng)時(shí),擴(kuò)展類的功能。
  2. 動(dòng)態(tài)增加功能,動(dòng)態(tài)撤銷

裝飾器模式和代理模式的區(qū)別

  • 代理是全權(quán)代理,目標(biāo)根本不對(duì)外,全部由代理類來完成;裝飾是增強(qiáng),是輔助,目標(biāo)仍然可以自行對(duì)外提供服務(wù),裝飾器只起增強(qiáng)作用。
  • 裝飾器模式強(qiáng)調(diào)的是: 增強(qiáng)、新增行為 ;代理模式強(qiáng)調(diào)的是: 對(duì)代理的對(duì)象施加控制,但不對(duì)對(duì)象本身的功能進(jìn)行增強(qiáng) 。
  • 裝飾器模式:生效的對(duì)象還是原本的對(duì)象;代理模式:生效的是新的對(duì)象(代理對(duì)象)

圖片
裝飾器和代理的區(qū)別

裝飾器的簡(jiǎn)單實(shí)現(xiàn)

場(chǎng)景 :天氣太熱了,喝點(diǎn)兒冰水解解暑;加點(diǎn)兒檸檬片,讓果汁好喝點(diǎn)兒

先定義一個(gè)喝水的接口

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

寫一個(gè)接口的實(shí)現(xiàn)

public class DrinkWater implements Drink {

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

}

一個(gè)簡(jiǎn)單的裝飾器

public class DrinkDecorator implements Drink {

    private final Drink drink;

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

    @Override
    public void drink() {
        System.out.println("先加點(diǎn)兒檸檬片");
        drink.drink();
    }

}

開始測(cè)試

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

運(yùn)行結(jié)果

先加點(diǎn)兒檸檬片
喝水

一個(gè)簡(jiǎn)單的裝飾器模式例子就寫完了;當(dāng)然這種例子在實(shí)際項(xiàng)目中肯定是用不到的,這里只是先了解一下裝飾器模式

裝飾器模式實(shí)戰(zhàn)

場(chǎng)景: 項(xiàng)目一期開發(fā)的時(shí)候,并沒有給鑒權(quán)部分設(shè)置緩存;二期開發(fā)考慮到性能問題,想要給鑒權(quán)部分加上緩存,這里就選擇了使用裝飾器模式進(jìn)行處理;

這里使用的緩存是spring的 spring-cache,不了解沒關(guān)系,知道幾個(gè)注解什么意思就行

@Cacheable 表示要對(duì)方法返回值進(jìn)行緩存

@CacheEvict 刪除緩存注解

為了簡(jiǎn)潔,以下代碼均為偽代碼

首先,需要一個(gè)權(quán)限的接口和實(shí)現(xiàn)類

public interface IDataAccessor {
    /**
     * 根據(jù)部門上級(jí) id 獲取所有子集部門
     */
    Set< Long > deptFindAllChildrenByParentIds(Collection< Long > parentIds);

    /**
     * 獲取數(shù)據(jù)范圍內(nèi)的部門
     */
    Set< Long > deptFindScopeById(Long userId);

實(shí)現(xiàn)類(注意這里加了@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);
    }
}

接下來就是對(duì)之前的代碼進(jìn)行裝飾,定義一個(gè)裝飾器的實(shí)現(xiàn)類

(這個(gè)類沒有 @Component, 沒有直接交給spring管理;加了注解會(huì)報(bào)錯(cuò):找到了2個(gè)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);
    }
}

接下來還需要將這個(gè)裝飾器的類注冊(cè)到spring中

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

根據(jù)業(yè)務(wù),維護(hù)緩存更新;這里使用的監(jiān)聽部門和員工的變更事件

@Component
public class DataScopeEvict {

    /**
     * 清空部門相關(guān)緩存
     */
    @CacheEvict(cacheNames = {"dept:parentId"}, allEntries = true)
    public void department() {
    }

    /**
     * 清空用戶相關(guān)緩存
     */
    @CacheEvict(cacheNames = {"dept:scope:userId"}, allEntries = true)
    public void user() {
    }
}
@Component
public class ScopeDataEventListener {
    @Autowired
    private DataScopeEvict evict;
    
 /**
     * 監(jiān)聽部門變更事件
     */
    @EventListener
    public void departmentEvent(DepartmentChangeEvent event) {
        // 1 增加 2 刪除 3 上級(jí)部門變更
        evict.department();
    }

    /**
     * 監(jiān)聽user變更事件
     */
    @EventListener
    public void userEvent(UserChangeEvent event) {
        // 2 刪除 3 主部門變更
        if (event.getType().equals(2) || event.getType().equals(3)) {
            evict.user();
        }
    }
}

一切準(zhǔn)備就緒,使用的時(shí)候直接使用裝飾器類就好了

@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);
    }
    
}

以上就是一個(gè)將裝飾器模式應(yīng)用到實(shí)際項(xiàng)目的例子;

在這個(gè)例子中,使用裝飾器模式增強(qiáng)了原本的代碼,不修改原本的代碼,原本的代碼也能正確提供服務(wù),只不過沒有使用緩存;只要方法名命名一致,只需修改注入的字段就可以升級(jí)完成,升級(jí)成本還是很低的。

這波使用裝飾器模式加緩存的操作寫到項(xiàng)目中,直接讓你的代碼 B ge pull full

小結(jié)

雖然使用裝飾器模式看起來B格高,但還是要注意自己項(xiàng)目的場(chǎng)景,選擇適合的方式解決問題。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8598

    瀏覽量

    151153
  • 緩存
    +關(guān)注

    關(guān)注

    1

    文章

    240

    瀏覽量

    26678
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68611
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    適配器模式代理模式區(qū)別

      代理模式  組成:  抽象角色:通過接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法?! ?b class='flag-5'>代理角色:實(shí)現(xiàn)抽象角色,是真實(shí)角色的代理,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法,并可以附加自己的
    發(fā)表于 10-22 15:17

    MCU模式和RGB模式區(qū)別

    模式(也寫成MPU模式的)。只有TFT模塊才有RGB接口。但應(yīng)用比較多的就是MCU模式和RGB模式,區(qū)別有以下幾點(diǎn):1.MCU接口:會(huì)解碼命
    發(fā)表于 11-03 08:53

    適配器模式、裝飾模式、代理模式區(qū)別

    適配器模式裝飾模式、代理模式都屬于設(shè)計(jì)模式中的結(jié)
    發(fā)表于 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'>區(qū)別</b>

    適配器模式代理模式區(qū)別

    適配器模式:適配器模式有時(shí)候也稱包裝樣式或者包裝。將一個(gè)類的接口轉(zhuǎn)接成用戶所期待的。代理模式:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。
    發(fā)表于 01-12 11:56 ?5275次閱讀
    適配器<b class='flag-5'>模式</b>和<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的<b class='flag-5'>區(qū)別</b>

    適配器模式裝飾模式區(qū)別

    裝飾模式指的是在不必改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能。它是通過創(chuàng)建一個(gè)包裝對(duì)象,也就是裝飾來包裹真實(shí)的對(duì)象。在計(jì)算機(jī)編程中,適配器模式(有時(shí)候也稱包裝樣式或者包
    發(fā)表于 01-15 10:31 ?7002次閱讀
    適配器<b class='flag-5'>模式</b>和<b class='flag-5'>裝飾</b><b class='flag-5'>模式</b>的<b class='flag-5'>區(qū)別</b>

    西門子PLC的等時(shí)模式和非等時(shí)模式區(qū)別

    西門子PLC的等時(shí)模式和非等時(shí)模式區(qū)別說明。
    發(fā)表于 04-23 15:58 ?4次下載

    LCD MCU模式和RGB模式

    應(yīng)用比較多的就是MUC模式和RGB模式區(qū)別有以下幾點(diǎn):1.MCU接口:會(huì)解碼命令,由timing generator產(chǎn)生時(shí)序信號(hào),驅(qū)動(dòng)COM和SEG驅(qū)。RGB接口:在寫LCD re
    發(fā)表于 10-28 09:50 ?19次下載
    LCD MCU<b class='flag-5'>模式</b>和RGB<b class='flag-5'>模式</b>

    GoF給裝飾模式的定義

    的源碼,就會(huì)發(fā)現(xiàn) middleware 功能的實(shí)現(xiàn)用的就是裝飾模式(Decorator Pattern)。
    的頭像 發(fā)表于 06-29 10:22 ?816次閱讀

    GoF設(shè)計(jì)模式代理模式

    它是一個(gè)使用率非常高的設(shè)計(jì)模式,在現(xiàn)實(shí)生活中,也是很常見。比如,演唱會(huì)門票黃牛。假設(shè)你需要看一場(chǎng)演唱會(huì),但官網(wǎng)上門票已經(jīng)售罄,于是就當(dāng)天到現(xiàn)場(chǎng)通過黃牛高價(jià)買了一張。在這個(gè)例子中,黃牛就相當(dāng)于演唱會(huì)門票的代理,在正式渠道無法購買門票的情況下,你通過
    的頭像 發(fā)表于 10-17 09:45 ?939次閱讀

    嵌入式C語言軟件設(shè)計(jì)之裝飾模式(Decorator Pattern)

    裝飾模式(Decorator Pattern),是結(jié)構(gòu)型設(shè)計(jì)模式的一種,裝飾
    發(fā)表于 03-01 10:55 ?412次閱讀

    演示裝飾模式的用法

    裝飾模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是
    的頭像 發(fā)表于 06-08 11:16 ?574次閱讀
    演示<b class='flag-5'>裝飾</b><b class='flag-5'>器</b><b class='flag-5'>模式</b>的用法

    設(shè)計(jì)模式結(jié)構(gòu)性:代理模式

    代理模式(Proxy Pattern)中,一個(gè)類代表另一個(gè)類的功能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。
    的頭像 發(fā)表于 06-09 15:27 ?840次閱讀
    設(shè)計(jì)<b class='flag-5'>模式</b>結(jié)構(gòu)性:<b class='flag-5'>代理</b><b class='flag-5'>模式</b>

    設(shè)計(jì)模式代理模式的使用場(chǎng)景

    設(shè)計(jì)模式在我看來更像是一種設(shè)計(jì)思維或設(shè)計(jì)思想,它就像《孫子兵法》一樣,為你的項(xiàng)目工程提供方向,讓你的項(xiàng)目工程更加健壯、靈活,延續(xù)生命力。本文即將分享的是設(shè)計(jì)模式的其中一種:代理模式。
    的頭像 發(fā)表于 10-08 14:34 ?1035次閱讀
    設(shè)計(jì)<b class='flag-5'>模式</b>中<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的使用場(chǎng)景

    示波器滾動(dòng)模式與標(biāo)準(zhǔn)模式區(qū)別

    示波器滾動(dòng)模式與標(biāo)準(zhǔn)模式區(qū)別? 示波器是一種電子測(cè)試儀器,它用于顯示電壓隨時(shí)間變化的波形圖。示波器可以設(shè)置為兩種顯示模式:滾動(dòng)模式和標(biāo)準(zhǔn)
    的頭像 發(fā)表于 11-07 10:13 ?2211次閱讀

    網(wǎng)絡(luò)橋接模式是什么? 網(wǎng)絡(luò)橋接模式和路由模式區(qū)別

    網(wǎng)絡(luò)橋接模式是一種網(wǎng)絡(luò)連接方式,它可以將多個(gè)設(shè)備連接在一起,使它們可以相互通信。在網(wǎng)絡(luò)橋接模式下,每個(gè)設(shè)備都可以直接與其他設(shè)備通信,而不需要經(jīng)過路由或其他中間設(shè)備。這種連接方式通常用于局域網(wǎng)中
    的頭像 發(fā)表于 05-10 13:48 ?4312次閱讀