成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何優雅的使用裝飾器模式

開發 前端
裝飾器模式(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();
}
}
}

一切準備就緒,使用的時候直接使用裝飾器類就好了;

@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技術指北
相關推薦

2010-02-01 17:50:32

Python裝飾器

2023-09-04 13:14:00

裝飾器設計模式

2022-01-19 08:21:12

設計裝飾器模式

2021-07-12 10:24:36

Go裝飾器代碼

2023-12-13 13:28:16

裝飾器模式Python設計模式

2022-05-10 09:12:16

TypeScript裝飾器

2021-01-13 05:50:44

迭代器 javascript設計模式

2024-03-08 08:00:00

Python開發裝飾器

2015-11-26 10:53:45

LinuxWindowsMac OS

2017-07-26 11:32:50

NETRabbitMQ系統集成

2024-02-23 12:11:53

裝飾器模式對象

2021-06-03 09:18:25

裝飾器模式包裝

2024-04-10 12:27:43

Python設計模式開發

2020-10-16 11:48:06

服務器系統運維

2023-06-16 09:08:39

ReactContextRFC

2022-09-19 23:04:08

Python裝飾器語言

2025-01-22 15:58:46

2024-09-12 15:32:35

裝飾器Python

2021-03-28 09:17:18

JVM場景鉤子函數

2024-11-04 15:30:43

Python裝飾器函數
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 天天干天天爱天天爽 | 81精品国产乱码久久久久久 | 中文字幕一区二区三区四区五区 | 美女131mm久久爽爽免费 | 精品国产色 | 亚洲视频一 | 欧美精品区 | 97人人澡人人爽91综合色 | 精品国产乱码久久久久久丨区2区 | 欧美中文字幕一区二区 | 精品国产乱码久久久久久蜜臀 | 成人久草 | 99re视频在线 | 国产91久久久久蜜臀青青天草二 | 久久专区 | 国产精品高潮呻吟久久 | 福利视频三区 | 99精品观看 | 99久久日韩精品免费热麻豆美女 | 欧美精品在线播放 | 亚洲精品欧美精品 | 色婷综合网 | 国产精品视频 | 日韩av一区二区在线观看 | 在线观看免费av网 | 天天干狠狠干 | 国产精品久久久久久久久久三级 | 欧美在线a | 超碰在线97国产 | 91中文字幕在线 | 男女污污动态图 | 欧美亚洲视频在线观看 | 亚洲欧美日韩精品久久亚洲区 | 欧美成人精品一区二区男人看 | 日本精品久久 | 亭亭五月激情 | 黄色av免费网站 | 免费黄色成人 | 国产美女久久久 | 欧美三级在线 | 国产精品亚洲综合 |