Java設(shè)計(jì)模式之裝飾模式詳解
多級(jí)緩存
在實(shí)際開發(fā)項(xiàng)目,為了減少數(shù)據(jù)庫(kù)的訪問(wèn)壓力,都會(huì)將數(shù)據(jù)緩存到內(nèi)存中
比如:Redis(分布式緩存)、EHCHE(JVM內(nèi)置緩存).
例如在早期中,項(xiàng)目比較小可能不會(huì)使用Redis做為緩存,使用JVM內(nèi)置的緩存框架,項(xiàng)目比較大的時(shí)候開始采用Redis分布式緩存框架,這時(shí)候需要設(shè)計(jì)一級(jí)與二級(jí)緩存。
緩存機(jī)制
1、JVM內(nèi)置緩存:將數(shù)據(jù)緩存在當(dāng)前的jvm中,缺陷:占用我們的JVM內(nèi)存,存在內(nèi)存溢出問(wèn)題,集群很難保證各個(gè)節(jié)點(diǎn)之間數(shù)據(jù)同步問(wèn)題。
2、分布式緩存:Redis,數(shù)據(jù)可以集群共享
裝飾模式
不改變?cè)写a的基礎(chǔ)之上,新增附加功能 ,mybatis、hibernate的二級(jí)緩存都屬于開發(fā)者自己去擴(kuò)展功能。
裝飾模式與代理模式區(qū)別
裝飾模式對(duì)我們的裝飾對(duì)象實(shí)現(xiàn)增強(qiáng),而代理模式及對(duì)我們目標(biāo)對(duì)象實(shí)現(xiàn)增強(qiáng)。
類圖
- Component (抽象構(gòu)件)
抽象構(gòu)件它是具體構(gòu)件和抽象裝飾類的共同父類,聲明了在具體構(gòu)件中實(shí)現(xiàn)的業(yè)務(wù)方法。
- ConcreteComponent (具體構(gòu)件 [被裝飾類] )
具體構(gòu)件ConcreteComponent是最核心、最原始、最基本的接口或抽象類的實(shí)現(xiàn),要裝飾的就是它。
- Decorator (抽象裝飾類)
抽象裝飾類也是抽象構(gòu)件類的子類,用于給具體構(gòu)件增加職責(zé),但是具體職責(zé)在其子類中實(shí)現(xiàn)。它維護(hù)一個(gè)指向抽象構(gòu)件對(duì)象的引用,通過(guò)該引用可以調(diào)用裝飾之前構(gòu)件對(duì)象的方法,并通過(guò)其子類擴(kuò)展該方法,以達(dá)到裝飾的目的。
- ConcreteDecorator ( 具體裝飾類)
具體裝飾類是抽象裝飾類的子類,負(fù)責(zé)向構(gòu)件添加新的職責(zé)。每一個(gè)具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴(kuò)充對(duì)象的行為。
實(shí)現(xiàn)裝飾器模式思路:
使用場(chǎng)景
- 需要擴(kuò)展一個(gè)類的功能時(shí)
- 需要?jiǎng)討B(tài)地給一個(gè)對(duì)象增加功能,并可以動(dòng)態(tài)地撤銷時(shí)
- 需要為一批的兄弟類進(jìn)行改裝或加裝功能時(shí)
優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 裝飾類和被裝飾類可以獨(dú)立發(fā)展,而不會(huì)相互耦合。它有效地把類的核心職責(zé)和裝飾功能分開了
- 裝飾模式是繼承關(guān)系的一一個(gè)替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的對(duì)象還是Component,實(shí)現(xiàn)的還是is-a的關(guān)系。
- 裝飾模式可以動(dòng)態(tài)地?cái)U(kuò)展一個(gè)實(shí)現(xiàn)類的功能
缺點(diǎn)
- 使用裝飾模式進(jìn)行系統(tǒng)設(shè)計(jì)時(shí)將產(chǎn)生很多小對(duì)象,這些對(duì)象的區(qū)別在于它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,大量小對(duì)象的產(chǎn)生勢(shì)必會(huì)占用更多的系統(tǒng)資源,在-定程序上影響程序的性能。
- 裝飾模式提供了一種比繼承更加靈活機(jī)動(dòng)的解決方案,但同時(shí)也意味著比繼承更加易于出錯(cuò),排錯(cuò)也很困難,對(duì)于多次裝飾的對(duì)象,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級(jí)排查,較為繁瑣。
實(shí)現(xiàn)邏輯
裝飾者類內(nèi)部含有被裝飾者(組合關(guān)系),且被裝飾者與裝飾者都繼承自共同的父類。這樣可以通過(guò)將被裝飾者的子類實(shí)例對(duì)象 傳入-> 裝飾者子類的實(shí)例對(duì)象中,拓展被裝飾者繼承類即可實(shí)現(xiàn)動(dòng)態(tài)的將新功能 附加到裝飾者子類實(shí)例對(duì)象上。在對(duì)象功能擴(kuò)展方面,它比繼承更有彈性,裝飾者模式也體現(xiàn)了開閉原則(ocp)。
使用裝飾模式實(shí)現(xiàn)二級(jí)緩存
設(shè)計(jì)思路
代碼案例
1、準(zhǔn)備兩個(gè)工具類(jvm緩存和redis緩存)
public class JvmMapCacheUtils { /** * 緩存容器 */ private static Map<String, String> caches = new ConcurrentHashMap<>(); public static <T> T getEntity(String key, Class<T> t) { // 緩存存放對(duì)象的情況 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ù)對(duì)應(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)件)也是一級(jí)緩存
userMapper.findByUser(1):這里查詢數(shù)據(jù)庫(kù)的代碼就不提供。
@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) { // 先查詢我們的一級(jí)緩存(Jvm內(nèi)置) T jvmUser = JvmMapCacheUtils.getEntity(key,t); if (jvmUser != null) { return (T) jvmUser; } // 查詢我們的db 通過(guò)aop直接獲取到我們的目標(biāo)對(duì)象方法 try { Object resultDb = joinPoint.proceed(); // 數(shù)據(jù)庫(kù)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ù)庫(kù)查詢數(shù)據(jù) * @Date 8:02 2022/7/6 */ @Override public <T> T getCacheEntity(String key, Class<T> t ) { // 先查詢我們的一級(jí)緩存(Jvm內(nèi)置) T jvmUser = JvmMapCacheUtils.getEntity(key,t); if (jvmUser != null) { return (T) jvmUser; } // 查詢我們的db 通過(guò)aop直接獲取到我們的目標(biāo)對(duì)象方法 UserEntity byUser = userMapper.findByUser(1); if(byUser == null){ return null; } // 數(shù)據(jù)庫(kù)DB有的情況 將該內(nèi)容緩存到當(dāng)前Jvm中 JvmMapCacheUtils.putEntity(key, byUser); return (T) byUser; } }
4、編寫redis二級(jí)緩存(這里使用繼承的方式)
@Component public class RedisDecorate extends JvmComponentCache implements AbstractDecorate { @Autowired private RedisUtils redisUtils; @Override public <T> T getCacheEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) { // 查詢我們的二級(jí)緩存 // 先查詢二級(jí)緩存 T redisUser = redisUtils.getEntity(key, t); if (redisUser != null) { return (T) redisUser; } // 如果一級(jí)緩存存在數(shù)據(jù) T jvmUser = super.getCacheEntity(key, t, joinPoint); if (jvmUser == null) { return null; } // 將該緩存數(shù)據(jù)放入到二級(jí)緩存中 redisUtils.putEntity(key, jvmUser); return (T) jvmUser; } }
5、使用裝飾過(guò)后的緩存類
@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的部分類圖
通過(guò)圖中可以看出:
- 抽象構(gòu)件(Component)角色:由InputStream扮演。這是-個(gè)抽象類,為各種子類型提供統(tǒng)一的接口。
- 具體構(gòu)件(ConcreteComponent)角色:由ByteArrayInputStream、 FilelnputStream、 PipedInputStream、StringBufferlnputStream等類扮演。它們實(shí)現(xiàn)了抽象構(gòu)件角色所規(guī)定的接口。
- 抽象裝飾(Decorator)角色:由FilterInputStream扮演。它實(shí)現(xiàn)了InputStream所規(guī)定的接口。
- 具體裝飾(ConcreteDecorator)角色:由幾個(gè)類扮演,分別是BufferedInputStream、DatalnputStream以及 兩個(gè)不常用到的類LineNumberlnputStream、PushbackInputStream。
到此這篇關(guān)于Java設(shè)計(jì)模式之裝飾模式詳解的文章就介紹到這了,更多相關(guān)Java裝飾模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)兩個(gè)隨機(jī)數(shù)組合并進(jìn)行排序的方法
本文主要介紹了Java實(shí)現(xiàn)兩個(gè)隨機(jī)數(shù)組合并進(jìn)行排序的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Java修飾符 abstract,static,final 的區(qū)別詳解
以下是對(duì)Java修飾符abstract,static,final的區(qū)別進(jìn)行了詳細(xì)的介紹,需要的朋友可以過(guò)來(lái)參考下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面試常見問(wèn)題---ConcurrentHashMap知識(shí),一起看看吧2021-06-06全面了解JAVA_BaseDAO數(shù)據(jù)處理類
下面小編就為大家?guī)?lái)一篇全面了解JAVA_BaseDAO數(shù)據(jù)處理類。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07JAVA中跳出當(dāng)前多重嵌套循環(huán)的方法詳解
今天在看面試題時(shí),發(fā)現(xiàn)了這個(gè)問(wèn)題,因?yàn)樵赑HP中跳出多次循環(huán)可以使用break數(shù)字來(lái)跳出多層循環(huán),但這在java中并不好使,這篇文章主要給大家介紹了關(guān)于JAVA中跳出當(dāng)前多重嵌套循環(huán)的相關(guān)資料,需要的朋友可以參考下2022-01-01Java 數(shù)組聲明、創(chuàng)建、初始化詳解
本文主要介紹Java 數(shù)組聲明、創(chuàng)建、初始化的資料,這里整理相關(guān)知識(shí),及簡(jiǎn)單實(shí)現(xiàn)代碼,幫助大家學(xué)習(xí),有興趣的小伙伴可以參考下2016-09-09mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法
本文主要介紹了mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01Spring Data JPA調(diào)用存儲(chǔ)過(guò)程實(shí)例代碼
本篇文章主要介紹了Spring Data JPA調(diào)用存儲(chǔ)過(guò)程實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04