深入解析Guava源碼:7大設(shè)計模式全解析

2024-12-30 18:30 更新

最近有小伙伴在 Guava 組件的使用上交流了一些問題,組件的使用很簡單,優(yōu)秀的人不僅僅在使用,學(xué)習(xí) Guava 的源碼設(shè)計是提高自己編程思想和能力的關(guān)鍵,跟著高手走,吃喝啥都有,跟著高手混,未來一定順。哈哈,下面 V 哥從 Guava 源碼中使用到的設(shè)計模式來詳細介紹一下,希望能幫助你更好的理解設(shè)計模式的精髓,開整。

Guava 源碼中使用到的設(shè)計模式主要包括以下幾種:

  1. 建造者模式(Builder Pattern):在 Guava 中,CacheBuilder 類就是使用了建造者模式,它允許用戶通過鏈式調(diào)用方法來設(shè)置緩存的各種參數(shù),如初始容量、最大大小、過期時間等,最后通過 build() 方法構(gòu)建并返回一個緩存實例。這種模式使得構(gòu)建過程非常清晰,并且易于維護和擴展。

  1. 代理模式(Proxy Pattern):Guava 中的 ForwardingCollection 類是代理模式的一個應(yīng)用,它提供了一個默認的代理實現(xiàn),使得用戶在實現(xiàn)自己的代理類時,可以只關(guān)注自己關(guān)心的方法,其他方法可以委托給被代理的對象來完成。

  1. 不可變模式(Immutable Pattern):Guava 提供了一系列不可變的集合類,如 ImmutableList、ImmutableSetImmutableMap 等。這些類確保了集合一旦創(chuàng)建,其內(nèi)容就不能被修改。這種模式在多線程環(huán)境中非常有用,因為它可以避免并發(fā)修改的問題,提高代碼的安全性和簡潔性。

  1. 單例模式(Singleton Pattern):雖然在搜索結(jié)果中沒有直接提及,但 Guava 的 LoadingCache 中的 CacheLoader 可以視為單例模式的一種應(yīng)用,它確保了緩存加載器實例的唯一性。

  1. 裝飾器模式(Decorator Pattern):Guava 中的 ForwardingObject 類可以看作是裝飾器模式的一個基礎(chǔ)實現(xiàn),它允許向一個對象動態(tài)地添加額外的職責,而不需要修改它的類定義。

  1. 適配器模式(Adapter Pattern):類似于裝飾器模式,Guava 中的 Forwarding 類也可以用于適配器模式,它提供了一個轉(zhuǎn)換接口,使得一個類的實例能夠作為另一個接口的實例使用。

  1. 觀察者模式(Observer Pattern):Guava 的 RemovalListener 可以視為觀察者模式的體現(xiàn),它允許監(jiān)聽緩存項的移除事件,從而進行相應(yīng)的處理。

這些設(shè)計模式在 Guava 框架中的應(yīng)用中大大提高了代碼的可讀性、可維護性和擴展性。下面 V 哥來一一詳細介紹。

1. 建造者模式(Builder Pattern)

CacheBuilder 類在 Guava 框架中是建造者模式的一個典型應(yīng)用。以下是對 CacheBuilder 類中建造者模式實現(xiàn)的分析,包括其實現(xiàn)過程和步驟。

CacheBuilder 類的建造者模式實現(xiàn):

  1. 定義建造者類CacheBuilder 類本身作為建造者,提供了一系列的方法來設(shè)置緩存的各種參數(shù)。

  1. 設(shè)置參數(shù)的方法CacheBuilder 提供了鏈式調(diào)用的方法來設(shè)置緩存的配置,例如 initialCapacity、maximumSize、expireAfterWriteremovalListener 等。

  1. 返回緩存實例:設(shè)置完所有參數(shù)后,調(diào)用 build() 方法來返回一個根據(jù)這些參數(shù)構(gòu)建的緩存實例。

代碼示例:

為了更好的理解,我們來簡化模擬一個CacheBuilder類的實現(xiàn)。

public class CacheBuilder<K, V> {
    private long expireAfterWriteNanos = -1;
    private long expireAfterAccessNanos = -1;
    private int initialCapacity = 16;
    private float concurrencyLevel = -1;
    private long maximumSize = Long.MAX_VALUE;
    private RemovalListener<? super K, ? super V> removalListener;
    private CacheLoader<? super K, V> loader;
    // ... 省略其他成員變量和方法 ...


    public CacheBuilder<K, V> initialCapacity(int initialCapacity) {
        this.initialCapacity = initialCapacity;
        return this; // 鏈式調(diào)用
    }


    public CacheBuilder<K, V> maximumSize(long maxSize) {
        this.maximumSize = maxSize;
        return this; // 鏈式調(diào)用
    }


    public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
        this.expireAfterWriteNanos = unit.toNanos(duration);
        return this; // 鏈式調(diào)用
    }


    // ... 省略其他設(shè)置方法 ...


    public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
        // 檢查參數(shù)有效性
        if (concurrencyLevel > MAX_SEGMENTS) {
            throw new IllegalArgumentException("concurrencyLevel cannot be greater than " + MAX_SEGMENTS);
        }
        // 構(gòu)建并返回緩存實例
        return new LocalCache<K, V>(this);
    }


    // ... 省略其他方法 ...
}

實現(xiàn)過程和步驟:

以上代碼的實現(xiàn)地這程和步驟是這樣滴,一起來 lock lock。

  1. 無參構(gòu)造函數(shù)CacheBuilder 類通常有一個無參的構(gòu)造函數(shù),用于創(chuàng)建建造者對象。

  1. 設(shè)置參數(shù):通過公共的方法設(shè)置緩存的各種參數(shù)。每個設(shè)置方法都接受一個參數(shù),并返回建造者對象自身的引用,允許鏈式調(diào)用。

  1. 參數(shù)校驗:在 build() 方法中進行參數(shù)的有效性校驗,確保緩存可以被正確構(gòu)建。

  1. 構(gòu)建緩存實例build() 方法根據(jù)前面設(shè)置的參數(shù)來創(chuàng)建并返回一個緩存實例。在 Guava 中,這個實例是 LocalCache 類型的對象。

  1. 鏈式調(diào)用:建造者模式的關(guān)鍵在于鏈式調(diào)用,使得設(shè)置參數(shù)的過程非常清晰和流暢。

  1. 靈活性和擴展性:如果未來需要添加新的配置參數(shù),只需在 CacheBuilder 中添加新的方法,并在 build() 方法中進行相應(yīng)的處理即可,無需修改其他調(diào)用 CacheBuilder 的代碼。

建造者模式在 CacheBuilder 類中的應(yīng)用,使得創(chuàng)建緩存實例的過程非常靈活和易于管理,同時保證了代碼的清晰性和可維護性。

2. 代理模式(Proxy Pattern)

ForwardingCollection 類是 Guava 庫中一個典型的代理模式的應(yīng)用。以下是對 ForwardingCollection 類中代理模式實現(xiàn)的分析,包括實現(xiàn)過程和步驟。

ForwardingCollection 類的代理模式實現(xiàn):

  1. 定義抽象方法ForwardingCollection 類提供了一個抽象方法 delegate(),它需要被子類實現(xiàn)以返回被包裝的集合對象。

  1. 轉(zhuǎn)發(fā)方法ForwardingCollection 類實現(xiàn)了 Collection 接口中的所有方法,并且在每個方法中,通過調(diào)用 delegate() 方法來獲取被包裝的集合對象,并將操作委托給它。

  1. 子類擴展:通過繼承 ForwardingCollection 類并實現(xiàn) delegate() 方法,用戶可以在子類中添加額外的功能,而不需要修改原始的集合類。

代碼示例:

還是上代碼吧,這樣更好理解一些(不要問我代碼誰)

public abstract class ForwardingCollection<E> extends ForwardingObject implements Collection<E> {
    @Override
    protected abstract Collection<E> delegate();


    @Override
    public boolean add(E element) {
        return delegate().add(element);
    }


    @Override
    public boolean remove(Object object) {
        return delegate().remove(object);
    }


    @Override
    public boolean contains(Object object) {
        return delegate().contains(object);
    }


    @Override
    public int size() {
        return delegate().size();
    }


    // ... 省略其他 Collection 接口方法的默認實現(xiàn) ...


    // 可以覆蓋的方法,例如添加額外日志的 add 方法
    public boolean add(E element) {
        boolean added = super.add(element);
        log("Added element: " + element);
        return added;
    }
}


public class LoggingCollection<E> extends ForwardingCollection<E> {
    private final Collection<E> delegate;


    public LoggingCollection(Collection<E> delegate) {
        this.delegate = delegate;
    }


    @Override
    protected Collection<E> delegate() {
        return delegate;
    }


    // 可以添加額外的方法或者覆蓋已有方法來添加日志功能
}

實現(xiàn)過程和步驟:

以下是實現(xiàn)過程和步驟的解析,一起來看一下。

  1. 定義委托方法:在 ForwardingCollection 中定義一個抽象的 delegate() 方法,強制要求子類實現(xiàn)它,以提供被代理的集合實例。

  1. 實現(xiàn)接口方法:為 Collection 接口中的每個方法提供一個默認實現(xiàn),這些實現(xiàn)通過調(diào)用 delegate() 方法來轉(zhuǎn)發(fā)操作。

  1. 創(chuàng)建子類:創(chuàng)建一個子類,比如 LoggingCollection,繼承自 ForwardingCollection。

  1. 實現(xiàn)委托方法:在子類中實現(xiàn) delegate() 方法,返回實際的集合對象。

  1. 添加額外功能:在子類中添加額外的功能,例如在添加元素時打印日志。

  1. 覆蓋默認方法:如果需要,可以覆蓋 ForwardingCollection 中的默認方法來改變行為。

  1. 使用包裝后的集合:使用 LoggingCollection 作為任何 Collection 接口的實現(xiàn),它將委托所有操作給原始集合,并在操作時添加日志功能。

通過這種方式,ForwardingCollection 類提供了一種靈活的方法來為現(xiàn)有的集合對象添加額外的功能,而不需要修改原始的集合代碼。這是代理模式的核心優(yōu)勢,即增加職責而不影響原有對象的結(jié)構(gòu),你 get 到了么。

3. 不可變模式(Immutable Pattern)

在 Guava 庫中,ImmutableListImmutableSetImmutableMap 類實現(xiàn)了不可變模式(Immutable Pattern),確保了集合一旦創(chuàng)建,其狀態(tài)(包含的元素)就不能被修改。以下是對這些類中不可變模式實現(xiàn)的分析,包括實現(xiàn)過程和步驟。

不可變模式的實現(xiàn)要點:

  1. 所有元素在構(gòu)造時設(shè)置:不可變集合類通過構(gòu)造函數(shù)接收所有初始化所需的元素,并在構(gòu)造過程中構(gòu)建最終的集合狀態(tài)。

  1. 沒有修改方法:不可變集合類不提供任何修改集合狀態(tài)的方法,如 add、removeclear 等。

  1. 提供復(fù)制構(gòu)造函數(shù):為了創(chuàng)建包含新元素的集合,提供復(fù)制構(gòu)造函數(shù),它允許在現(xiàn)有集合的基礎(chǔ)上添加或替換元素,并返回一個新的不可變集合實例。

  1. 使用內(nèi)部靜態(tài)工廠方法:提供靜態(tài)工廠方法來創(chuàng)建集合實例,這些方法通常使用內(nèi)部的構(gòu)建器模式來收集元素。

  1. 使用 final 關(guān)鍵詞:集合內(nèi)部存儲元素的數(shù)據(jù)結(jié)構(gòu)被聲明為 final,確保它們一旦被初始化就不能被重新賦值。

  1. 深度不可變性:如果集合包含其他對象的引用,確保這些對象也是不可變的,或者在添加到集合之前進行深拷貝。

ImmutableList類

以下是 ImmutableList 類的關(guān)鍵代碼示例,展示了不可變模式的一些關(guān)鍵實現(xiàn):

public final class ImmutableList<E> extends ImmutableCollection<E> implements List<E> {
    private final transient Object[] array; // 存儲元素的數(shù)組


    // 私有構(gòu)造函數(shù),通過內(nèi)部的 Builder 類來設(shè)置元素
    private ImmutableList(Object[] array) {
        this.array = array;
    }


    // 公共靜態(tài)工廠方法,用于創(chuàng)建 ImmutableList 實例
    public static <E> ImmutableList<E> of() {
        return new ImmutableList<E>(new Object[0]);
    }


    public static <E> ImmutableList<E> of(E... elements) {
        return new ImmutableList<E>(copyOf(elements));
    }


    // 復(fù)制數(shù)組的工具方法,確保輸入數(shù)組的不可變性
    private static <E> Object[] copyOf(E[] elements) {
        Object[] array = new Object[elements.length];
        System.arraycopy(elements, 0, array, 0, elements.length);
        return array;
    }


    // 沒有提供修改集合的方法,如 add 或 remove


    // 提供元素訪問的方法
    public E get(int index) {
        return (E) array[index];
    }


    // ... 省略其他 List 接口方法的實現(xiàn) ...
}

實現(xiàn)過程和步驟:

繼續(xù)解釋實現(xiàn)過程和步驟哈。

  1. 定義存儲結(jié)構(gòu):定義一個 final 的數(shù)組或集合來存儲元素。

  1. 私有構(gòu)造函數(shù):提供一個私有構(gòu)造函數(shù),它接受所有初始化所需的元素。

  1. 靜態(tài)工廠方法:提供公共的靜態(tài)工廠方法,用于創(chuàng)建不可變集合的實例。

  1. 元素復(fù)制:在構(gòu)造函數(shù)中,對傳入的元素數(shù)組進行復(fù)制,以確保輸入的數(shù)組本身不會被修改。

  1. 禁止修改操作:不提供任何修改集合狀態(tài)的公共方法。

  1. 提供訪問方法:提供訪問集合元素的方法,如 get。

  1. 確保線程安全:由于集合狀態(tài)不可變,天然線程安全,不需要額外的同步措施。

  1. 創(chuàng)建子類或變體:如果需要提供特定類型的不可變集合,可以創(chuàng)建子類或使用不同的靜態(tài)工廠方法。

ImmutableSet類

ImmutableSet 類在 Guava 庫中同樣是不可變集合的一個實現(xiàn),提供了一個不允許修改的 Set 集合。下面是 ImmutableSet 類關(guān)鍵實現(xiàn)的分析:

關(guān)鍵特性:

  1. 基于 CollectionsImmutableSet 通常是基于其他不可變集合,如 ImmutableList 或者另一個 ImmutableSet,來實現(xiàn)的。

  1. 構(gòu)造函數(shù)私有化:為了防止狀態(tài)被修改,ImmutableSet 的構(gòu)造函數(shù)是私有的,只能通過靜態(tài)工廠方法來創(chuàng)建實例。

  1. 使用 Hash Table:內(nèi)部使用一個合適的數(shù)據(jù)結(jié)構(gòu)(如 ImmutableMap 的鍵集)來存儲元素,保證元素的唯一性。

  1. 不提供修改操作:不提供任何添加、刪除或清空集合的方法。

  1. 返回新實例:對于看似修改操作的方法,如 addremove,實際上會返回一個新的 ImmutableSet 實例。

  1. 迭代器安全:提供了安全的迭代器,不允許通過迭代器修改集合。

代碼示例:

public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements Set<E> {
    // 內(nèi)部使用 ImmutableMap 來存儲元素
    private transient ImmutableMap<E, Boolean> map;


    protected ImmutableSet(ImmutableMap<E, Boolean> map) {
        this.map = map;
    }


    // 公共靜態(tài)工廠方法,用于創(chuàng)建 ImmutableSet 實例
    public static <E> ImmutableSet<E> of() {
        return new RegularSet<>(ImmutableMap.of());
    }


    public static <E> ImmutableSet<E> of(E element) {
        return new SingletonSet<>(element));
    }


    public static <E> ImmutableSet<E> copyOf(Collection<? extends E> elements) {
        return new RegularSet<>(ImmutableMap.copyOf(elements));
    }


    // 不提供修改集合的方法,如 add 或 remove


    // 提供元素訪問的方法
    public boolean contains(Object object) {
        return map.containsKey(object);
    }


    // 返回新實例而不是修改當前集合
    public ImmutableSet<E> add(E element) {
        throw new UnsupportedOperationException();
    }


    public ImmutableSet<E> remove(E element) {
        throw new UnsupportedOperationException();
    }


    // ... 省略其他 Set 接口方法的實現(xiàn) ...
}

實現(xiàn)過程和步驟:

  1. 定義內(nèi)部數(shù)據(jù)結(jié)構(gòu):定義一個內(nèi)部的 ImmutableMap 來存儲元素和對應(yīng)的布爾值(通常為 true),因為 Set 需要唯一性。

  1. 私有構(gòu)造函數(shù):構(gòu)造函數(shù)是私有的,只接受一個 ImmutableMap 對象。

  1. 靜態(tài)工廠方法:提供公共的靜態(tài)工廠方法,如 ofcopyOf,用于創(chuàng)建 ImmutableSet 實例。

  1. 元素檢查:實現(xiàn) contains 方法,通過檢查 map 是否包含元素來確定集合是否包含該對象。

  1. 禁止修改操作:不實現(xiàn) add、remove 等修改集合的方法,或者在這些方法中拋出 UnsupportedOperationException。

  1. 返回新實例:如果需要執(zhí)行看似修改操作的方法,創(chuàng)建并返回一個新的 ImmutableSet 實例。

  1. 提供迭代器:提供一個迭代器來遍歷集合,但不允許修改集合。

  1. 確保不可變:確保所有內(nèi)部數(shù)據(jù)結(jié)構(gòu)都是不可變的,并且在任何對外提供訪問的包裝方法中,都不暴露可變視圖。

ImmutableMap

ImmutableMap 類在 Guava 庫中是不可變集合模式的一個重要實現(xiàn),提供了一個不允許修改的 Map 集合。以下是 ImmutableMap 類關(guān)鍵實現(xiàn)的分析:

關(guān)鍵特性:

  1. 基于 HashMap 或 TreeMapImmutableMap 通常是基于一個不可變的 HashMapTreeMap 來實現(xiàn)的。

  1. 構(gòu)造函數(shù)私有化:為了防止狀態(tài)被修改,ImmutableMap 的構(gòu)造函數(shù)是私有的,只能通過靜態(tài)工廠方法來創(chuàng)建實例。

  1. 不提供修改操作:不提供任何添加、刪除或清空映射的方法。

  1. 返回新實例:對于看似修改操作的方法,如 putremove,實際上會返回一個新的 ImmutableMap 實例。

  1. 迭代器安全:提供了安全的 entrySet(), keySet(), 和 values() 迭代器,不允許通過迭代器修改集合。

  1. 使用 final 關(guān)鍵詞:存儲鍵值對的數(shù)據(jù)結(jié)構(gòu)被聲明為 final,確保它們一旦被初始化就不能被重新賦值。

代碼示例:

public abstract class ImmutableMap<K, V> implements Map<K, V> {
    // 存儲鍵值對的數(shù)組或其它數(shù)據(jù)結(jié)構(gòu)
    private final transient Entry<K, V>[] entries;


    protected ImmutableMap(Entry<K, V>[] entries) {
        this.entries = entries;
    }


    // 公共靜態(tài)工廠方法,用于創(chuàng)建 ImmutableMap 實例
    public static <K, V> ImmutableMap<K, V> of() {
        return new RegularImmutableMap<>(EMPTY_ENTRY_ARRAY);
    }


    public static <K, V> ImmutableMap<K, V> of(K key, V value) {
        return new SingletonImmutableMap<>(key, value));
    }


    public static <K, V> ImmutableMap<K, V> copyOf(Map<? extends K, ? extends V> map) {
        return new RegularImmutableMap<>(copyEntries(map));
    }


    // 不提供修改集合的方法,如 put 或 remove
    public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }


    public V remove(Object key) {
        throw new UnsupportedOperationException();
    }


    // 提供元素訪問的方法
    public V get(Object key) {
        // 根據(jù)鍵查找值的邏輯
    }


    // 返回新實例而不是修改當前映射
    public ImmutableMap<K, V> putAll(Map<? extends K, ? extends V> map) {
        throw new UnsupportedOperationException();
    }


    // ... 省略其他 Map 接口方法的實現(xiàn) ...


    // 提供迭代器
    public Iterator<Entry<K, V>> entryIterator() {
        return new UnmodifiableIterator<>() {
            public Entry<K, V> next() {
                // 返回不可變的 Entry
            }
        };
    }
}

實現(xiàn)過程和步驟:

  1. 定義內(nèi)部數(shù)據(jù)結(jié)構(gòu):定義一個內(nèi)部的數(shù)組或其他數(shù)據(jù)結(jié)構(gòu)來存儲鍵值對。

  1. 私有構(gòu)造函數(shù):構(gòu)造函數(shù)是私有的,只接受一個包含所有鍵值對的數(shù)組。

  1. 靜態(tài)工廠方法:提供公共的靜態(tài)工廠方法,如 ofcopyOf,用于創(chuàng)建 ImmutableMap 實例。

  1. 元素檢查:實現(xiàn) get 方法,通過遍歷內(nèi)部存儲結(jié)構(gòu)來查找并返回鍵對應(yīng)的值。

  1. 禁止修改操作:不實現(xiàn) put、remove 等修改映射的方法,或者在這些方法中拋出 UnsupportedOperationException。

  1. 返回新實例:如果需要執(zhí)行看似修改操作的方法,創(chuàng)建并返回一個新的 ImmutableMap 實例。

  1. 提供迭代器:提供不可變的迭代器來遍歷鍵值對、鍵或值。

  1. 確保不可變:確保所有內(nèi)部數(shù)據(jù)結(jié)構(gòu)都是不可變的,并且在任何對外提供訪問的包裝方法中,都不暴露可變視圖。

  1. 實現(xiàn)視圖方法:為 entrySet(), keySet(), 和 values() 提供實現(xiàn),確保返回的視圖不提供修改原映射的能力。

劃重點,一句話小結(jié)一下這3個類:

ImmutableList、ImmutableSetImmutableMap 類提供了一個安全、不可變的 Map 集合實現(xiàn),適用于多線程環(huán)境和需要確保集合狀態(tài)不會被改變的場景。

4. 單例模式(Singleton Pattern)

在 Guava 的 LoadingCache 中,CacheLoader 接口本身并不直接實現(xiàn)單例模式,但 CacheLoader 的實現(xiàn)可以是單例的。CacheLoader 接口用于定義加載緩存項的邏輯,當緩存未命中時,LoadingCache 將使用 CacheLoader 來加載數(shù)據(jù)。

然而,CacheLoader 的一個常見實現(xiàn),MoreExecutors.listeningDecorator,實際上使用了單例模式。以下是對使用 MoreExecutors.listeningDecorator 作為 CacheLoader 的單例實現(xiàn)的分析:

單例模式的實現(xiàn)要點:

  1. 單例類:創(chuàng)建一個類,控制實例的創(chuàng)建,確保全局只存在一個實例。

  1. 私有構(gòu)造函數(shù):使構(gòu)造函數(shù)私有,防止外部通過 new 來創(chuàng)建實例。

  1. 提供全局訪問點:提供一個公共的靜態(tài)方法,返回類的唯一實例。

  1. 延遲初始化:如果需要,可以在實例被使用時才創(chuàng)建它,實現(xiàn)延遲初始化。

代碼示例:

public final class MoreExecutors {
    private MoreExecutors() {
        // 私有構(gòu)造函數(shù),防止實例化
    }


    public static ListeningExecutorService listeningDecorator(ExecutorService executor) {
        if (executor instanceof ListeningExecutorService) {
            return (ListeningExecutorService) executor;
        }
        return new ListeningDecorator(executor);
    }


    private static class ListeningDecorator extends AbstractListeningExecutorService {
        // ListeningDecorator 的具體實現(xiàn)
    }
}

實現(xiàn)過程和步驟:

  1. 創(chuàng)建私有構(gòu)造函數(shù):在 MoreExecutors 類中創(chuàng)建一個私有構(gòu)造函數(shù),確保不能通過 new 關(guān)鍵字來創(chuàng)建實例。

  1. 提供靜態(tài)方法:提供一個公共的靜態(tài)方法 listeningDecorator,該方法接受一個 ExecutorService 參數(shù)。

  1. 檢查參數(shù)類型:在 listeningDecorator 方法中,檢查傳入的 ExecutorService 是否已經(jīng)實現(xiàn)了 ListeningExecutorService 接口。

  1. 返回現(xiàn)有實例或創(chuàng)建新實例:如果傳入的 ExecutorService 已經(jīng)是一個 ListeningExecutorService,則直接返回它;否則,創(chuàng)建一個新的 ListeningDecorator 實例。

  1. 實現(xiàn)單例邏輯ListeningDecorator 類作為內(nèi)部靜態(tài)類,確保了 MoreExecutors.listeningDecorator 方法每次調(diào)用時返回的都是同一個 ListeningDecorator 實例。

  1. 使用單例:在 LoadingCache 的構(gòu)建過程中,使用 MoreExecutors.listeningDecorator 來獲取單例的 ListeningExecutorService

  1. 線程安全:由于 ListeningDecorator 是一個靜態(tài)類,它的實例化是線程安全的,并且在第一次創(chuàng)建后,后續(xù)的調(diào)用都會返回同一個實例。

通過這種方式,MoreExecutors.listeningDecorator 實現(xiàn)了單例模式,確保了無論何時何地調(diào)用該方法,都只會創(chuàng)建一個 ListeningExecutorService 的裝飾實例。這在多線程環(huán)境中非常有用,因為它可以避免創(chuàng)建不必要的線程池實例,并確保所有線程共享同一個線程池。

5. 裝飾器模式(Decorator Pattern)

在 Guava 庫中,ForwardingObject 類是裝飾器模式的一個應(yīng)用。裝飾器模式允許用戶在不修改對象自身的基礎(chǔ)上,向一個對象添加額外的職責。ForwardingObject 作為一個抽象類,提供了一個基礎(chǔ)的裝飾器實現(xiàn),它將所有方法調(diào)用轉(zhuǎn)發(fā)到被裝飾對象上。

以下是 ForwardingObject 類中裝飾器模式的實現(xiàn)分析,包括實現(xiàn)過程和步驟:

裝飾器模式的實現(xiàn)要點:

  1. 定義抽象裝飾器類:創(chuàng)建一個抽象類 ForwardingObject,它繼承自 Forwarding 類,并實現(xiàn) Object 接口。

  1. 定義被裝飾對象的引用:在 ForwardingObject 類中定義一個類型為被裝飾類的引用。

  1. 提供構(gòu)造函數(shù):提供一個構(gòu)造函數(shù),用于在創(chuàng)建裝飾器實例時注入被裝飾對象。

  1. 轉(zhuǎn)發(fā)方法:實現(xiàn) Object 類的方法,如 equals, hashCode, 和 toString,將這些方法調(diào)用轉(zhuǎn)發(fā)到被裝飾對象。

  1. 提供抽象方法:定義一個抽象方法,如 delegate(),要求子類實現(xiàn)以返回被裝飾對象。

  1. 子類擴展:通過繼承 ForwardingObject 并實現(xiàn)抽象方法,用戶可以在子類中添加額外的邏輯。

代碼示例:

public abstract class ForwardingObject extends Forwarding {
    final Object delegate;


    protected ForwardingObject(Object delegate) {
        this.delegate = delegate;
    }


    @Override
    protected Object delegate() {
        return delegate;
    }


    @Override
    public boolean equals(Object obj) {
        return delegate.equals(obj);
    }


    @Override
    public int hashCode() {
        return delegate.hashCode();
    }


    @Override
    public String toString() {
        return delegate.toString();
    }

    
    // 其他需要轉(zhuǎn)發(fā)的方法...
}

實現(xiàn)過程和步驟:

  1. 創(chuàng)建抽象裝飾器類:定義 ForwardingObject 類,繼承自 Forwarding 類,后者提供了默認的轉(zhuǎn)發(fā)實現(xiàn)。

  1. 定義被裝飾對象的引用:在 ForwardingObject 類中定義一個 final 引用 delegate,用于存儲被裝飾對象。

  1. 提供構(gòu)造函數(shù):提供一個構(gòu)造函數(shù),接受一個參數(shù)并賦值給 delegate 引用。

  1. 實現(xiàn)抽象方法:實現(xiàn) delegate() 方法,返回被裝飾對象的引用。

  1. 轉(zhuǎn)發(fā) Object 方法:重寫 equals, hashCode, 和 toString 方法,將調(diào)用轉(zhuǎn)發(fā)到 delegate 對象。

  1. 擴展裝飾器:用戶創(chuàng)建一個繼承自 ForwardingObject 的子類,并實現(xiàn)所需的額外邏輯。

  1. 使用裝飾器:實例化裝飾器子類,并向其構(gòu)造函數(shù)中傳入被裝飾的對象。

  1. 保持接口一致性:確保裝飾器類與被裝飾類有相同的接口,這樣客戶端代碼就可以透明地使用裝飾器。

通過這種方式,ForwardingObject 類提供了一個靈活的裝飾器模式實現(xiàn),允許用戶在運行時動態(tài)地添加額外的職責,而不需要修改原有的對象。這種模式在擴展功能、增加日志記錄、緩存等場景下非常有用。

6. 適配器模式(Adapter Pattern)

在 Guava 庫中,Forwarding 不是直接作為一個適配器模式的實現(xiàn)而存在,而是一個抽象基類,被用作簡化裝飾器模式、代理模式或適配器模式的實現(xiàn)。Forwarding 類通過委托機制,使得子類可以自定義委托給另一個對象的行為。

然而,Guava 中的 Forwarding 類似概念可以應(yīng)用于適配器模式。適配器模式將一個類的接口轉(zhuǎn)換成客戶期望的另一個接口,使原本由于接口不兼容而不能一起工作的類可以一起工作。

以下是 Forwarding 類中適配器模式實現(xiàn)的分析:

適配器模式的實現(xiàn)要點:

  1. 定義抽象基類:創(chuàng)建一個抽象類 Forwarding,作為所有轉(zhuǎn)發(fā)類的基類。

  1. 定義委托方法:在 Forwarding 類中定義一個抽象方法 delegate(),用于返回被包裝或適配的對象。

  1. 實現(xiàn)默認行為:在 Forwarding 類中為所有繼承自委托對象的方法提供一個默認的實現(xiàn)。

  1. 子類擴展:通過繼承 Forwarding 類并實現(xiàn) delegate() 方法,用戶可以在子類中指定具體的被適配對象。

  1. 接口轉(zhuǎn)換:在子類中,可以添加方法或覆蓋現(xiàn)有方法,以轉(zhuǎn)換或添加接口方法。

代碼示例:

public abstract class Forwarding {
    // 抽象方法,由子類實現(xiàn),返回被委托的對象
    protected abstract Object delegate();


    // 示例:轉(zhuǎn)發(fā) equals 方法
    @Override
    public boolean equals(Object obj) {
        return delegate().equals(obj);
    }


    // 示例:轉(zhuǎn)發(fā) hashCode 方法
    @Override
    public int hashCode() {
        return delegate().hashCode();
    }


    // 示例:轉(zhuǎn)發(fā) toString 方法
    @Override
    public String toString() {
        return delegate().toString();
    }


    // 其他方法...
}

實現(xiàn)過程和步驟:

  1. 定義抽象基類:創(chuàng)建 Forwarding 類,作為一個抽象基類提供轉(zhuǎn)發(fā)邏輯。

  1. 定義委托方法:在 Forwarding 類中定義 delegate() 方法,它是一個抽象方法,由子類實現(xiàn)以返回實際被操作的對象。

  1. 實現(xiàn)默認行為:為 Object 類的方法如 equals、hashCodetoString 提供默認實現(xiàn),將調(diào)用轉(zhuǎn)發(fā)到 delegate() 方法返回的對象。

  1. 擴展子類:創(chuàng)建一個子類,繼承自 Forwarding 并實現(xiàn) delegate() 方法,指定被適配的對象。

  1. 接口轉(zhuǎn)換:在子類中實現(xiàn)需要適配的接口方法,將這些方法的調(diào)用轉(zhuǎn)發(fā)到被適配對象的相應(yīng)方法。

  1. 使用適配器:客戶端代碼通過 Forwarding 子類實例與被適配對象交互,從而實現(xiàn)接口轉(zhuǎn)換。

通過這種方式,Forwarding 類的設(shè)計模式可以作為適配器模式的一個實現(xiàn)基礎(chǔ),允許開發(fā)者通過繼承和委托機制,將一個類的接口轉(zhuǎn)換成另一種形式,滿足不同的接口需求。這在兼容舊接口、整合異構(gòu)系統(tǒng)、或者提供額外功能時非常有用。

7. 觀察者模式(Observer Pattern)

在 Guava 庫中,RemovalListener 接口本身并不直接實現(xiàn)適配器模式,但它可以用作適配器模式的一部分。RemovalListener 是 Guava 緩存框架中的一個組件,用于監(jiān)聽緩存項的移除事件。當緩存項由于任何原因被移除時(例如,由于容量限制或超時),RemovalListener 可以接收通知并執(zhí)行相應(yīng)的操作。

以下是如何使用 RemovalListener 接口來實現(xiàn)適配器模式的分析:

適配器模式的實現(xiàn)要點:

  1. 定義客戶端接口:定義客戶端使用的接口,這通常是 Java 標準庫中的接口,如 Map。

  1. 定義目標接口:定義需要適配的目標接口,這可以是第三方庫的接口,或者是自定義的接口。

  1. 創(chuàng)建適配器類:創(chuàng)建一個適配器類,實現(xiàn)客戶端接口,并在內(nèi)部持有目標接口的實例。

  1. 轉(zhuǎn)發(fā)方法調(diào)用:在適配器類中,實現(xiàn)客戶端接口的方法,并將調(diào)用轉(zhuǎn)發(fā)到目標對象的相應(yīng)方法。

  1. 添加額外的邏輯:在轉(zhuǎn)發(fā)過程中,可以在適配器類中添加額外的邏輯,如事件監(jiān)聽。

代碼示例:

public class MyCustomMap<K, V> implements Map<K, V> {
    private final Map<K, V> delegate; // 目標對象
    private final RemovalListener<K, V> listener; // 適配器模式中的額外邏輯


    public MyCustomMap(Map<K, V> delegate) {
        this.delegate = delegate;
        this.listener = new MyRemovalListener();
    }


    // 將 MyCustomMap 的方法調(diào)用轉(zhuǎn)發(fā)到 delegate
    @Override
    public V put(K key, V value) {
        // 在添加新值之前,可能需要執(zhí)行一些邏輯
        return delegate.put(key, value);
    }


    // ... 其他 Map 方法的實現(xiàn) ...


    // 自定義的 RemovalListener 實現(xiàn)
    private class MyRemovalListener implements RemovalListener<K, V> {
        @Override
        public void onRemoval(RemovalNotification<K, V> notification) {
            // 當緩存項被移除時,執(zhí)行額外的邏輯
            if (notification.getCause() == RemovalCause.REPLACED) {
                // 處理被替換項的邏輯
            }
            // ... 其他邏輯 ...
        }
    }
}

實現(xiàn)過程和步驟:

  1. 定義目標對象:創(chuàng)建一個 Map 類型的字段 delegate,它是需要適配的目標對象。

  1. 定義 RemovalListener:創(chuàng)建一個內(nèi)部類 MyRemovalListener 實現(xiàn) RemovalListener 接口,并添加自定義的邏輯。

  1. 創(chuàng)建適配器類:創(chuàng)建 MyCustomMap 類,實現(xiàn) Map 接口,并在構(gòu)造函數(shù)中接收一個 Map 對象。

  1. 轉(zhuǎn)發(fā)方法調(diào)用:在 MyCustomMap 類中,實現(xiàn) Map 接口的方法,并將調(diào)用轉(zhuǎn)發(fā)到 delegate 對象。

  1. 注冊 RemovalListener:在創(chuàng)建緩存時,將 MyRemovalListener 注冊為緩存的移除監(jiān)聽器。

  1. 添加額外邏輯:在 MyRemovalListeneronRemoval 方法中,根據(jù)移除原因添加額外的邏輯。

  1. 使用適配器:客戶端代碼創(chuàng)建 MyCustomMap 實例,并像使用普通 Map 一樣使用它,同時享受額外的移除事件監(jiān)聽功能。

通過這種方式,RemovalListener 可以作為適配器模式的一部分,使得 MyCustomMap 類在遵循 Map 接口的同時,增加了對緩存項移除事件的監(jiān)聽能力。這種模式在需要擴展現(xiàn)有類的功能時非常有用,特別是當無法直接修改現(xiàn)有類時。

最后

以上是 V 哥在學(xué)習(xí) Guava 源碼中總結(jié)的7個設(shè)計模式的實現(xiàn)分析,歡迎關(guān)注威哥愛編程,做自己的技術(shù),讓別人去卷吧。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號