首頁 > 軟體

Java設計模式之裝飾模式詳解

2022-07-27 14:02:45

多級快取

在實際開發專案,為了減少資料庫的存取壓力,都會將資料快取到記憶體中

比如:Redis(分散式快取)、EHCHE(JVM內建快取).

例如在早期中,專案比較小可能不會使用Redis做為快取,使用JVM內建的快取框架,專案比較大的時候開始採用Redis分散式快取框架,這時候需要設計一級與二級快取。

快取機制

1、JVM內建快取:將資料快取在當前的jvm中,缺陷:佔用我們的JVM記憶體,存在記憶體溢位問題,叢集很難保證各個節點之間資料同步問題。

2、分散式快取:Redis,資料可以叢集共用

裝飾模式

不改變原有程式碼的基礎之上,新增附加功能 ,mybatis、hibernate的二級快取都屬於開發者自己去擴充套件功能。

裝飾模式與代理模式區別

裝飾模式對我們的裝飾物件實現增強,而代理模式及對我們目標物件實現增強。

類圖

  • Component (抽象構件)

抽象構件它是具體構件和抽象裝飾類的共同父類別,宣告了在具體構件中實現的業務方法。

  • ConcreteComponent (具體構件 [被裝飾類] )

具體構件ConcreteComponent是最核心、最原始、最基本的介面或抽象類的實現,要裝飾的就是它。

  • Decorator (抽象裝飾類)

抽象裝飾類也是抽象構件類的子類,用於給具體構件增加職責,但是具體職責在其子類中實現。它維護一個指向抽象構件物件的參照,通過該參照可以呼叫裝飾之前構件物件的方法,並通過其子類擴充套件該方法,以達到裝飾的目的。

  • ConcreteDecorator ( 具體裝飾類)

具體裝飾類是抽象裝飾類的子類,負責向構件新增新的職責。每一個具體裝飾類都定義了一些新的行為,它可以呼叫在抽象裝飾類中定義的方法,並可以增加新的方法用以擴充物件的行為。

實現裝飾器模式思路:

使用場景

  • 需要擴充套件一個類的功能時
  • 需要動態地給一個物件增加功能,並可以動態地復原時
  • 需要為一批的兄弟類進行改裝或加裝功能時

優缺點

優點

  • 裝飾類和被裝飾類可以獨立發展,而不會相互耦合。它有效地把類的核心職責和裝飾功能分開了
  • 裝飾模式是繼承關係的一一個替代方案。我們看裝飾類Decorator,不管裝飾多少層,返回的物件還是Component,實現的還是is-a的關係。
  • 裝飾模式可以動態地擴充套件一個實現類的功能

缺點

  • 使用裝飾模式進行系統設計時將產生很多小物件,這些物件的區別在於它們之間相互連線的方式有所不同,而不是它們的類或者屬性值有所不同,大量小物件的產生勢必會佔用更多的系統資源,在-定程式上影響程式的效能。
  • 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味著比繼承更加易於出錯,排錯也很困難,對於多次裝飾的物件,偵錯時尋找錯誤可能需要逐級排查,較為繁瑣。

實現邏輯

裝飾者類內部含有被裝飾者(組合關係),且被裝飾者與裝飾者都繼承自共同的父類別。這樣可以通過將被裝飾者的子類範例物件 傳入-> 裝飾者子類的範例物件中,拓展被裝飾者繼承類即可實現動態的將新功能 附加到裝飾者子類範例物件上。在物件功能擴充套件方面,它比繼承更有彈性,裝飾者模式也體現了開閉原則(ocp)。

使用裝飾模式實現二級快取

設計思路

程式碼案例

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    資料
     */
    public void setString(String key, String data) {
        map.put(key, data);
    }
    /**
     * 根據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);
    }
    /**
     * 根據對應的key刪除key
     *
     * @param key
     */
    public void delKey(String key) {
        map.put(key, null);
    }
}

2、編寫快取介面和裝飾者抽象類(抽象構件),抽象類繼承介面

public interface ComponentCache {
    /**
     * 根據key查詢快取資料
     *
     * @param
     * @return
     */
    <T> T getCacheEntity(String key, Class<T> t);
}
public interface AbstractDecorate extends  ComponentCache {
}

3、編寫jvm快取(具體構件)也是一級快取

userMapper.findByUser(1):這裡查詢資料庫的程式碼就不提供。

@Component
public class JvmComponentCache implements ComponentCache {
    @Autowired
    private UserMapper userMapper;
    /**
     * @param key
     * @param t         返回的資料型別
     * @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內建)
        T jvmUser = JvmMapCacheUtils.getEntity(key,t);
        if (jvmUser != null) {
            return (T) jvmUser;
        }
//          查詢我們的db 通過aop直接獲取到我們的目標物件方法
        try {
            Object resultDb = joinPoint.proceed();
            // 資料庫DB有的情況 將該內容快取到當前Jvm中
            JvmMapCacheUtils.putEntity(key, resultDb);
            return (T) resultDb;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }
    /**
     * @Author kaico
     * @Description //TODO 直接查詢資料庫查詢資料
     * @Date 8:02 2022/7/6
     */
    @Override
    public <T> T getCacheEntity(String key, Class<T> t ) {
        // 先查詢我們的一級快取(Jvm內建)
        T jvmUser = JvmMapCacheUtils.getEntity(key,t);
        if (jvmUser != null) {
            return (T) jvmUser;
        }
        // 查詢我們的db 通過aop直接獲取到我們的目標物件方法
        UserEntity byUser = userMapper.findByUser(1);
        if(byUser == null){
            return null;
        }
        // 資料庫DB有的情況 將該內容快取到當前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;
        }
        // 如果一級快取存在資料
        T jvmUser = super.getCacheEntity(key, t, joinPoint);
        if (jvmUser == null) {
            return null;
        }
        // 將該快取資料放入到二級快取中
        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的部分類圖

通過圖中可以看出:

  • 抽象構件(Component)角色:由InputStream扮演。這是-個抽象類,為各種子型別提供統一的介面。
  • 具體構件(ConcreteComponent)角色:由ByteArrayInputStream、 FilelnputStream、 PipedInputStream、StringBufferlnputStream等類扮演。它們實現了抽象構件角色所規定的介面。
  • 抽象裝飾(Decorator)角色:由FilterInputStream扮演。它實現了InputStream所規定的介面。
  • 具體裝飾(ConcreteDecorator)角色:由幾個類扮演,分別是BufferedInputStream、DatalnputStream以及 兩個不常用到的類LineNumberlnputStream、PushbackInputStream。

到此這篇關於Java設計模式之裝飾模式詳解的文章就介紹到這了,更多相關Java裝飾模式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com