Java設(shè)計模式之裝飾模式詳解
多級緩存
在實際開發(fā)項目,為了減少數(shù)據(jù)庫的訪問壓力,都會將數(shù)據(jù)緩存到內(nèi)存中
比如:Redis(分布式緩存)、EHCHE(JVM內(nèi)置緩存).
例如在早期中,項目比較小可能不會使用Redis做為緩存,使用JVM內(nèi)置的緩存框架,項目比較大的時候開始采用Redis分布式緩存框架,這時候需要設(shè)計一級與二級緩存。

緩存機制
1、JVM內(nèi)置緩存:將數(shù)據(jù)緩存在當(dāng)前的jvm中,缺陷:占用我們的JVM內(nèi)存,存在內(nèi)存溢出問題,集群很難保證各個節(jié)點之間數(shù)據(jù)同步問題。
2、分布式緩存:Redis,數(shù)據(jù)可以集群共享
裝飾模式
不改變原有代碼的基礎(chǔ)之上,新增附加功能 ,mybatis、hibernate的二級緩存都屬于開發(fā)者自己去擴(kuò)展功能。
裝飾模式與代理模式區(qū)別
裝飾模式對我們的裝飾對象實現(xiàn)增強,而代理模式及對我們目標(biāo)對象實現(xiàn)增強。
類圖

- Component (抽象構(gòu)件)
抽象構(gòu)件它是具體構(gòu)件和抽象裝飾類的共同父類,聲明了在具體構(gòu)件中實現(xiàn)的業(yè)務(wù)方法。
- ConcreteComponent (具體構(gòu)件 [被裝飾類] )
具體構(gòu)件ConcreteComponent是最核心、最原始、最基本的接口或抽象類的實現(xiàn),要裝飾的就是它。
- Decorator (抽象裝飾類)
抽象裝飾類也是抽象構(gòu)件類的子類,用于給具體構(gòu)件增加職責(zé),但是具體職責(zé)在其子類中實現(xiàn)。它維護(hù)一個指向抽象構(gòu)件對象的引用,通過該引用可以調(diào)用裝飾之前構(gòu)件對象的方法,并通過其子類擴(kuò)展該方法,以達(dá)到裝飾的目的。
- ConcreteDecorator ( 具體裝飾類)
具體裝飾類是抽象裝飾類的子類,負(fù)責(zé)向構(gòu)件添加新的職責(zé)。每一個具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴(kuò)充對象的行為。
實現(xiàn)裝飾器模式思路:

使用場景
- 需要擴(kuò)展一個類的功能時
- 需要動態(tài)地給一個對象增加功能,并可以動態(tài)地撤銷時
- 需要為一批的兄弟類進(jìn)行改裝或加裝功能時
優(yōu)缺點
優(yōu)點
- 裝飾類和被裝飾類可以獨立發(fā)展,而不會相互耦合。它有效地把類的核心職責(zé)和裝飾功能分開了
- 裝飾模式是繼承關(guān)系的一一個替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的對象還是Component,實現(xiàn)的還是is-a的關(guān)系。
- 裝飾模式可以動態(tài)地擴(kuò)展一個實現(xiàn)類的功能
缺點
- 使用裝飾模式進(jìn)行系統(tǒng)設(shè)計時將產(chǎn)生很多小對象,這些對象的區(qū)別在于它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,大量小對象的產(chǎn)生勢必會占用更多的系統(tǒng)資源,在-定程序上影響程序的性能。
- 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味著比繼承更加易于出錯,排錯也很困難,對于多次裝飾的對象,調(diào)試時尋找錯誤可能需要逐級排查,較為繁瑣。
實現(xiàn)邏輯
裝飾者類內(nèi)部含有被裝飾者(組合關(guān)系),且被裝飾者與裝飾者都繼承自共同的父類。這樣可以通過將被裝飾者的子類實例對象 傳入-> 裝飾者子類的實例對象中,拓展被裝飾者繼承類即可實現(xiàn)動態(tài)的將新功能 附加到裝飾者子類實例對象上。在對象功能擴(kuò)展方面,它比繼承更有彈性,裝飾者模式也體現(xiàn)了開閉原則(ocp)。
使用裝飾模式實現(xiàn)二級緩存
設(shè)計思路

代碼案例
1、準(zhǔn)備兩個工具類(jvm緩存和redis緩存)
public class JvmMapCacheUtils {
/**
* 緩存容器
*/
private static Map<String, String> caches = new ConcurrentHashMap<>();
public static <T> T getEntity(String key, Class<T> t) {
// 緩存存放對象的情況
String json = caches.get(key);
return JSONObject.parseObject(json, t);
}
public static void putEntity(String key, Object o) {
String json = JSONObject.toJSONString(o);
caches.put(key, json);
}
}
@Component
public class RedisUtils {
private final Map<String, String> map = new ConcurrentHashMap<>();
/**
* 存放string類型
*
* @param key key
* @param data 數(shù)據(jù)
*/
public void setString(String key, String data) {
map.put(key, data);
}
/**
* 根據(jù)key查詢string類型
*
* @param key
* @return
*/
public String getString(String key) {
String value = map.get(key);
return value;
}
public <T> T getEntity(String key, Class<T> t) {
String json = getString(key);
return JSONObject.parseObject(json, t);
}
public void putEntity(String key, Object object) {
String json = JSONObject.toJSONString(object);
setString(key, json);
}
/**
* 根據(jù)對應(yīng)的key刪除key
*
* @param key
*/
public void delKey(String key) {
map.put(key, null);
}
}2、編寫緩存接口和裝飾者抽象類(抽象構(gòu)件),抽象類繼承接口
public interface ComponentCache {
/**
* 根據(jù)key查詢緩存數(shù)據(jù)
*
* @param
* @return
*/
<T> T getCacheEntity(String key, Class<T> t);
}
public interface AbstractDecorate extends ComponentCache {
}3、編寫jvm緩存(具體構(gòu)件)也是一級緩存
userMapper.findByUser(1):這里查詢數(shù)據(jù)庫的代碼就不提供。
@Component
public class JvmComponentCache implements ComponentCache {
@Autowired
private UserMapper userMapper;
/**
* @param key
* @param t 返回的數(shù)據(jù)類型
* @param joinPoint
* @return T
* @Author kaico
* @Description //TODO
* @Date 21:02 2022/7/5
* @Param
*/
@Override
public <T> T getCacheEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) {
// 先查詢我們的一級緩存(Jvm內(nèi)置)
T jvmUser = JvmMapCacheUtils.getEntity(key,t);
if (jvmUser != null) {
return (T) jvmUser;
}
// 查詢我們的db 通過aop直接獲取到我們的目標(biāo)對象方法
try {
Object resultDb = joinPoint.proceed();
// 數(shù)據(jù)庫DB有的情況 將該內(nèi)容緩存到當(dāng)前Jvm中
JvmMapCacheUtils.putEntity(key, resultDb);
return (T) resultDb;
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
/**
* @Author kaico
* @Description //TODO 直接查詢數(shù)據(jù)庫查詢數(shù)據(jù)
* @Date 8:02 2022/7/6
*/
@Override
public <T> T getCacheEntity(String key, Class<T> t ) {
// 先查詢我們的一級緩存(Jvm內(nèi)置)
T jvmUser = JvmMapCacheUtils.getEntity(key,t);
if (jvmUser != null) {
return (T) jvmUser;
}
// 查詢我們的db 通過aop直接獲取到我們的目標(biāo)對象方法
UserEntity byUser = userMapper.findByUser(1);
if(byUser == null){
return null;
}
// 數(shù)據(jù)庫DB有的情況 將該內(nèi)容緩存到當(dāng)前Jvm中
JvmMapCacheUtils.putEntity(key, byUser);
return (T) byUser;
}
}4、編寫redis二級緩存(這里使用繼承的方式)
@Component
public class RedisDecorate extends JvmComponentCache implements AbstractDecorate {
@Autowired
private RedisUtils redisUtils;
@Override
public <T> T getCacheEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) {
// 查詢我們的二級緩存
// 先查詢二級緩存
T redisUser = redisUtils.getEntity(key, t);
if (redisUser != null) {
return (T) redisUser;
}
// 如果一級緩存存在數(shù)據(jù)
T jvmUser = super.getCacheEntity(key, t, joinPoint);
if (jvmUser == null) {
return null;
}
// 將該緩存數(shù)據(jù)放入到二級緩存中
redisUtils.putEntity(key, jvmUser);
return (T) jvmUser;
}
}5、使用裝飾過后的緩存類
@Component
public class KaicoCache {
@Autowired
private RedisDecorate redisDecorate;
public <T> T getCacheEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) {
return redisDecorate.getCacheEntity(key, t, joinPoint);
}
}分析Java的jdk中的裝飾器模式
以下是Java I/O流InputStream的部分類圖

通過圖中可以看出:
- 抽象構(gòu)件(Component)角色:由InputStream扮演。這是-個抽象類,為各種子類型提供統(tǒng)一的接口。
- 具體構(gòu)件(ConcreteComponent)角色:由ByteArrayInputStream、 FilelnputStream、 PipedInputStream、StringBufferlnputStream等類扮演。它們實現(xiàn)了抽象構(gòu)件角色所規(guī)定的接口。
- 抽象裝飾(Decorator)角色:由FilterInputStream扮演。它實現(xiàn)了InputStream所規(guī)定的接口。
- 具體裝飾(ConcreteDecorator)角色:由幾個類扮演,分別是BufferedInputStream、DatalnputStream以及 兩個不常用到的類LineNumberlnputStream、PushbackInputStream。
到此這篇關(guān)于Java設(shè)計模式之裝飾模式詳解的文章就介紹到這了,更多相關(guān)Java裝飾模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
構(gòu)建SpringBoot+MyBatis+Freemarker的項目詳解
在本篇內(nèi)容里小編給大家整理的是關(guān)于構(gòu)建SpringBoot+MyBatis+Freemarker的項目的具體步驟以及實例代碼,需要的朋友們參考下。2019-06-06
淺談String、StringBuffer和StringBuilder之間的區(qū)別
這篇文章主要介紹了淺談String、StringBuffer和StringBuilder之間的區(qū)別,通過字面量方式為字符串賦值時,此時的字符串存儲在方法區(qū)的字符串常量池中,需要的朋友可以參考下2023-10-10
try-with-resource優(yōu)雅關(guān)閉io流的方法
這篇文章主要給大家介紹了關(guān)于try-with-resource優(yōu)雅關(guān)閉io流的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
java Socket編程實現(xiàn)I/O多路復(fù)用的示例
本文主要介紹了java Socket編程實現(xiàn)I/O多路復(fù)用的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09
java并發(fā)編程專題(一)----線程基礎(chǔ)知識
這篇文章主要介紹了java并發(fā)編程線程的基礎(chǔ)知識,文中講解非常詳細(xì),幫助大家更好的學(xué)習(xí)JAVA并發(fā)編程,感興趣想學(xué)習(xí)JAVA的可以了解下2020-06-06

