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ù)緩存在當前的jvm中,缺陷:占用我們的JVM內(nèi)存,存在內(nèi)存溢出問題,集群很難保證各個節(jié)點之間數(shù)據(jù)同步問題。
2、分布式緩存:Redis,數(shù)據(jù)可以集群共享
裝飾模式
不改變原有代碼的基礎(chǔ)之上,新增附加功能 ,mybatis、hibernate的二級緩存都屬于開發(fā)者自己去擴展功能。
裝飾模式與代理模式區(qū)別
裝飾模式對我們的裝飾對象實現(xiàn)增強,而代理模式及對我們目標對象實現(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)。它維護一個指向抽象構(gòu)件對象的引用,通過該引用可以調(diào)用裝飾之前構(gòu)件對象的方法,并通過其子類擴展該方法,以達到裝飾的目的。
- ConcreteDecorator ( 具體裝飾類)
具體裝飾類是抽象裝飾類的子類,負責(zé)向構(gòu)件添加新的職責(zé)。每一個具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴充對象的行為。
實現(xiàn)裝飾器模式思路:
使用場景
- 需要擴展一個類的功能時
- 需要動態(tài)地給一個對象增加功能,并可以動態(tài)地撤銷時
- 需要為一批的兄弟類進行改裝或加裝功能時
優(yōu)缺點
優(yōu)點
- 裝飾類和被裝飾類可以獨立發(fā)展,而不會相互耦合。它有效地把類的核心職責(zé)和裝飾功能分開了
- 裝飾模式是繼承關(guān)系的一一個替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的對象還是Component,實現(xiàn)的還是is-a的關(guān)系。
- 裝飾模式可以動態(tài)地擴展一個實現(xiàn)類的功能
缺點
- 使用裝飾模式進行系統(tǒng)設(shè)計時將產(chǎn)生很多小對象,這些對象的區(qū)別在于它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,大量小對象的產(chǎn)生勢必會占用更多的系統(tǒng)資源,在-定程序上影響程序的性能。
- 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味著比繼承更加易于出錯,排錯也很困難,對于多次裝飾的對象,調(diào)試時尋找錯誤可能需要逐級排查,較為繁瑣。
實現(xiàn)邏輯
裝飾者類內(nèi)部含有被裝飾者(組合關(guān)系),且被裝飾者與裝飾者都繼承自共同的父類。這樣可以通過將被裝飾者的子類實例對象 傳入-> 裝飾者子類的實例對象中,拓展被裝飾者繼承類即可實現(xiàn)動態(tài)的將新功能 附加到裝飾者子類實例對象上。在對象功能擴展方面,它比繼承更有彈性,裝飾者模式也體現(xiàn)了開閉原則(ocp)。
使用裝飾模式實現(xiàn)二級緩存
設(shè)計思路
代碼案例
1、準備兩個工具類(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直接獲取到我們的目標對象方法 try { Object resultDb = joinPoint.proceed(); // 數(shù)據(jù)庫DB有的情況 將該內(nèi)容緩存到當前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直接獲取到我們的目標對象方法 UserEntity byUser = userMapper.findByUser(1); if(byUser == null){ return null; } // 數(shù)據(jù)庫DB有的情況 將該內(nèi)容緩存到當前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)文章
Java實現(xiàn)兩個隨機數(shù)組合并進行排序的方法
本文主要介紹了Java實現(xiàn)兩個隨機數(shù)組合并進行排序的方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09Java修飾符 abstract,static,final 的區(qū)別詳解
以下是對Java修飾符abstract,static,final的區(qū)別進行了詳細的介紹,需要的朋友可以過來參考下2013-09-09解析ConcurrentHashMap: 預(yù)熱(內(nèi)部一些小方法分析)
ConcurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。Segment的結(jié)構(gòu)和HashMap類似,是一種數(shù)組和鏈表結(jié)構(gòu),今天給大家普及java面試常見問題---ConcurrentHashMap知識,一起看看吧2021-06-06全面了解JAVA_BaseDAO數(shù)據(jù)處理類
下面小編就為大家?guī)硪黄媪私釰AVA_BaseDAO數(shù)據(jù)處理類。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-07-07Java 數(shù)組聲明、創(chuàng)建、初始化詳解
本文主要介紹Java 數(shù)組聲明、創(chuàng)建、初始化的資料,這里整理相關(guān)知識,及簡單實現(xiàn)代碼,幫助大家學(xué)習(xí),有興趣的小伙伴可以參考下2016-09-09mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法
本文主要介紹了mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01Spring Data JPA調(diào)用存儲過程實例代碼
本篇文章主要介紹了Spring Data JPA調(diào)用存儲過程實例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-04-04