最近有小伙伴在 Guava 組件的使用上交流了一些問題,組件的使用很簡單,優(yōu)秀的人不僅僅在使用,學(xué)習(xí) Guava 的源碼設(shè)計是提高自己編程思想和能力的關(guān)鍵,跟著高手走,吃喝啥都有,跟著高手混,未來一定順。哈哈,下面 V 哥從 Guava 源碼中使用到的設(shè)計模式來詳細介紹一下,希望能幫助你更好的理解設(shè)計模式的精髓,開整。
Guava 源碼中使用到的設(shè)計模式主要包括以下幾種:
CacheBuilder
類就是使用了建造者模式,它允許用戶通過鏈式調(diào)用方法來設(shè)置緩存的各種參數(shù),如初始容量、最大大小、過期時間等,最后通過 build()
方法構(gòu)建并返回一個緩存實例。這種模式使得構(gòu)建過程非常清晰,并且易于維護和擴展。ForwardingCollection
類是代理模式的一個應(yīng)用,它提供了一個默認的代理實現(xiàn),使得用戶在實現(xiàn)自己的代理類時,可以只關(guān)注自己關(guān)心的方法,其他方法可以委托給被代理的對象來完成。ImmutableList
、ImmutableSet
和 ImmutableMap
等。這些類確保了集合一旦創(chuàng)建,其內(nèi)容就不能被修改。這種模式在多線程環(huán)境中非常有用,因為它可以避免并發(fā)修改的問題,提高代碼的安全性和簡潔性。LoadingCache
中的 CacheLoader
可以視為單例模式的一種應(yīng)用,它確保了緩存加載器實例的唯一性。ForwardingObject
類可以看作是裝飾器模式的一個基礎(chǔ)實現(xiàn),它允許向一個對象動態(tài)地添加額外的職責,而不需要修改它的類定義。Forwarding
類也可以用于適配器模式,它提供了一個轉(zhuǎn)換接口,使得一個類的實例能夠作為另一個接口的實例使用。RemovalListener
可以視為觀察者模式的體現(xiàn),它允許監(jiān)聽緩存項的移除事件,從而進行相應(yīng)的處理。這些設(shè)計模式在 Guava 框架中的應(yīng)用中大大提高了代碼的可讀性、可維護性和擴展性。下面 V 哥來一一詳細介紹。
CacheBuilder
類在 Guava 框架中是建造者模式的一個典型應(yīng)用。以下是對 CacheBuilder
類中建造者模式實現(xiàn)的分析,包括其實現(xiàn)過程和步驟。
CacheBuilder
類本身作為建造者,提供了一系列的方法來設(shè)置緩存的各種參數(shù)。CacheBuilder
提供了鏈式調(diào)用的方法來設(shè)置緩存的配置,例如 initialCapacity
、maximumSize
、expireAfterWrite
、removalListener
等。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)地這程和步驟是這樣滴,一起來 lock lock。
CacheBuilder
類通常有一個無參的構(gòu)造函數(shù),用于創(chuàng)建建造者對象。build()
方法中進行參數(shù)的有效性校驗,確保緩存可以被正確構(gòu)建。build()
方法根據(jù)前面設(shè)置的參數(shù)來創(chuàng)建并返回一個緩存實例。在 Guava 中,這個實例是 LocalCache
類型的對象。CacheBuilder
中添加新的方法,并在 build()
方法中進行相應(yīng)的處理即可,無需修改其他調(diào)用 CacheBuilder
的代碼。
建造者模式在 CacheBuilder
類中的應(yīng)用,使得創(chuàng)建緩存實例的過程非常靈活和易于管理,同時保證了代碼的清晰性和可維護性。
ForwardingCollection
類是 Guava 庫中一個典型的代理模式的應(yīng)用。以下是對 ForwardingCollection
類中代理模式實現(xiàn)的分析,包括實現(xiàn)過程和步驟。
ForwardingCollection
類提供了一個抽象方法 delegate()
,它需要被子類實現(xiàn)以返回被包裝的集合對象。ForwardingCollection
類實現(xiàn)了 Collection
接口中的所有方法,并且在每個方法中,通過調(diào)用 delegate()
方法來獲取被包裝的集合對象,并將操作委托給它。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)過程和步驟的解析,一起來看一下。
ForwardingCollection
中定義一個抽象的 delegate()
方法,強制要求子類實現(xiàn)它,以提供被代理的集合實例。Collection
接口中的每個方法提供一個默認實現(xiàn),這些實現(xiàn)通過調(diào)用 delegate()
方法來轉(zhuǎn)發(fā)操作。LoggingCollection
,繼承自 ForwardingCollection
。delegate()
方法,返回實際的集合對象。ForwardingCollection
中的默認方法來改變行為。LoggingCollection
作為任何 Collection
接口的實現(xiàn),它將委托所有操作給原始集合,并在操作時添加日志功能。
通過這種方式,ForwardingCollection
類提供了一種靈活的方法來為現(xiàn)有的集合對象添加額外的功能,而不需要修改原始的集合代碼。這是代理模式的核心優(yōu)勢,即增加職責而不影響原有對象的結(jié)構(gòu),你 get 到了么。
在 Guava 庫中,ImmutableList
、ImmutableSet
和 ImmutableMap
類實現(xiàn)了不可變模式(Immutable Pattern),確保了集合一旦創(chuàng)建,其狀態(tài)(包含的元素)就不能被修改。以下是對這些類中不可變模式實現(xiàn)的分析,包括實現(xiàn)過程和步驟。
add
、remove
或 clear
等。
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) ...
}
繼續(xù)解釋實現(xiàn)過程和步驟哈。
get
。
ImmutableSet類
ImmutableSet
類在 Guava 庫中同樣是不可變集合的一個實現(xiàn),提供了一個不允許修改的 Set 集合。下面是 ImmutableSet
類關(guān)鍵實現(xiàn)的分析:
ImmutableSet
通常是基于其他不可變集合,如 ImmutableList
或者另一個 ImmutableSet
,來實現(xiàn)的。ImmutableSet
的構(gòu)造函數(shù)是私有的,只能通過靜態(tài)工廠方法來創(chuàng)建實例。ImmutableMap
的鍵集)來存儲元素,保證元素的唯一性。add
或 remove
,實際上會返回一個新的 ImmutableSet
實例。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) ...
}
ImmutableMap
來存儲元素和對應(yīng)的布爾值(通常為 true
),因為 Set 需要唯一性。ImmutableMap
對象。of
和 copyOf
,用于創(chuàng)建 ImmutableSet
實例。contains
方法,通過檢查 map
是否包含元素來確定集合是否包含該對象。add
、remove
等修改集合的方法,或者在這些方法中拋出 UnsupportedOperationException
。ImmutableSet
實例。
ImmutableMap
類
ImmutableMap
類在 Guava 庫中是不可變集合模式的一個重要實現(xiàn),提供了一個不允許修改的 Map 集合。以下是 ImmutableMap
類關(guān)鍵實現(xiàn)的分析:
ImmutableMap
通常是基于一個不可變的 HashMap
或 TreeMap
來實現(xiàn)的。ImmutableMap
的構(gòu)造函數(shù)是私有的,只能通過靜態(tài)工廠方法來創(chuàng)建實例。put
或 remove
,實際上會返回一個新的 ImmutableMap
實例。entrySet()
, keySet()
, 和 values()
迭代器,不允許通過迭代器修改集合。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
}
};
}
}
of
和 copyOf
,用于創(chuàng)建 ImmutableMap
實例。get
方法,通過遍歷內(nèi)部存儲結(jié)構(gòu)來查找并返回鍵對應(yīng)的值。put
、remove
等修改映射的方法,或者在這些方法中拋出 UnsupportedOperationException
。ImmutableMap
實例。entrySet()
, keySet()
, 和 values()
提供實現(xiàn),確保返回的視圖不提供修改原映射的能力。劃重點,一句話小結(jié)一下這3個類:
ImmutableList
、ImmutableSet
、ImmutableMap
類提供了一個安全、不可變的 Map 集合實現(xiàn),適用于多線程環(huán)境和需要確保集合狀態(tài)不會被改變的場景。
在 Guava 的 LoadingCache
中,CacheLoader
接口本身并不直接實現(xiàn)單例模式,但 CacheLoader
的實現(xiàn)可以是單例的。CacheLoader
接口用于定義加載緩存項的邏輯,當緩存未命中時,LoadingCache
將使用 CacheLoader
來加載數(shù)據(jù)。
然而,CacheLoader
的一個常見實現(xiàn),MoreExecutors.listeningDecorator
,實際上使用了單例模式。以下是對使用 MoreExecutors.listeningDecorator
作為 CacheLoader
的單例實現(xiàn)的分析:
new
來創(chuàng)建實例。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)
}
}
MoreExecutors
類中創(chuàng)建一個私有構(gòu)造函數(shù),確保不能通過 new
關(guān)鍵字來創(chuàng)建實例。listeningDecorator
,該方法接受一個 ExecutorService
參數(shù)。listeningDecorator
方法中,檢查傳入的 ExecutorService
是否已經(jīng)實現(xiàn)了 ListeningExecutorService
接口。ExecutorService
已經(jīng)是一個 ListeningExecutorService
,則直接返回它;否則,創(chuàng)建一個新的 ListeningDecorator
實例。ListeningDecorator
類作為內(nèi)部靜態(tài)類,確保了 MoreExecutors.listeningDecorator
方法每次調(diào)用時返回的都是同一個 ListeningDecorator
實例。LoadingCache
的構(gòu)建過程中,使用 MoreExecutors.listeningDecorator
來獲取單例的 ListeningExecutorService
。ListeningDecorator
是一個靜態(tài)類,它的實例化是線程安全的,并且在第一次創(chuàng)建后,后續(xù)的調(diào)用都會返回同一個實例。
通過這種方式,MoreExecutors.listeningDecorator
實現(xiàn)了單例模式,確保了無論何時何地調(diào)用該方法,都只會創(chuàng)建一個 ListeningExecutorService
的裝飾實例。這在多線程環(huán)境中非常有用,因為它可以避免創(chuàng)建不必要的線程池實例,并確保所有線程共享同一個線程池。
在 Guava 庫中,ForwardingObject
類是裝飾器模式的一個應(yīng)用。裝飾器模式允許用戶在不修改對象自身的基礎(chǔ)上,向一個對象添加額外的職責。ForwardingObject
作為一個抽象類,提供了一個基礎(chǔ)的裝飾器實現(xiàn),它將所有方法調(diào)用轉(zhuǎn)發(fā)到被裝飾對象上。
以下是 ForwardingObject
類中裝飾器模式的實現(xiàn)分析,包括實現(xiàn)過程和步驟:
ForwardingObject
,它繼承自 Forwarding
類,并實現(xiàn) Object
接口。ForwardingObject
類中定義一個類型為被裝飾類的引用。Object
類的方法,如 equals
, hashCode
, 和 toString
,將這些方法調(diào)用轉(zhuǎn)發(fā)到被裝飾對象。delegate()
,要求子類實現(xiàn)以返回被裝飾對象。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ā)的方法...
}
ForwardingObject
類,繼承自 Forwarding
類,后者提供了默認的轉(zhuǎn)發(fā)實現(xiàn)。ForwardingObject
類中定義一個 final
引用 delegate
,用于存儲被裝飾對象。delegate
引用。delegate()
方法,返回被裝飾對象的引用。Object
方法:重寫 equals
, hashCode
, 和 toString
方法,將調(diào)用轉(zhuǎn)發(fā)到 delegate
對象。ForwardingObject
的子類,并實現(xiàn)所需的額外邏輯。
通過這種方式,ForwardingObject
類提供了一個靈活的裝飾器模式實現(xiàn),允許用戶在運行時動態(tài)地添加額外的職責,而不需要修改原有的對象。這種模式在擴展功能、增加日志記錄、緩存等場景下非常有用。
在 Guava 庫中,Forwarding
不是直接作為一個適配器模式的實現(xiàn)而存在,而是一個抽象基類,被用作簡化裝飾器模式、代理模式或適配器模式的實現(xiàn)。Forwarding
類通過委托機制,使得子類可以自定義委托給另一個對象的行為。
然而,Guava 中的 Forwarding
類似概念可以應(yīng)用于適配器模式。適配器模式將一個類的接口轉(zhuǎn)換成客戶期望的另一個接口,使原本由于接口不兼容而不能一起工作的類可以一起工作。
以下是 Forwarding
類中適配器模式實現(xiàn)的分析:
Forwarding
,作為所有轉(zhuǎn)發(fā)類的基類。Forwarding
類中定義一個抽象方法 delegate()
,用于返回被包裝或適配的對象。Forwarding
類中為所有繼承自委托對象的方法提供一個默認的實現(xiàn)。Forwarding
類并實現(xiàn) delegate()
方法,用戶可以在子類中指定具體的被適配對象。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();
}
// 其他方法...
}
Forwarding
類,作為一個抽象基類提供轉(zhuǎn)發(fā)邏輯。Forwarding
類中定義 delegate()
方法,它是一個抽象方法,由子類實現(xiàn)以返回實際被操作的對象。Object
類的方法如 equals
、hashCode
和 toString
提供默認實現(xiàn),將調(diào)用轉(zhuǎn)發(fā)到 delegate()
方法返回的對象。Forwarding
并實現(xiàn) delegate()
方法,指定被適配的對象。Forwarding
子類實例與被適配對象交互,從而實現(xiàn)接口轉(zhuǎn)換。
通過這種方式,Forwarding
類的設(shè)計模式可以作為適配器模式的一個實現(xiàn)基礎(chǔ),允許開發(fā)者通過繼承和委托機制,將一個類的接口轉(zhuǎn)換成另一種形式,滿足不同的接口需求。這在兼容舊接口、整合異構(gòu)系統(tǒng)、或者提供額外功能時非常有用。
在 Guava 庫中,RemovalListener
接口本身并不直接實現(xiàn)適配器模式,但它可以用作適配器模式的一部分。RemovalListener
是 Guava 緩存框架中的一個組件,用于監(jiān)聽緩存項的移除事件。當緩存項由于任何原因被移除時(例如,由于容量限制或超時),RemovalListener
可以接收通知并執(zhí)行相應(yīng)的操作。
以下是如何使用 RemovalListener
接口來實現(xiàn)適配器模式的分析:
Map
。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) {
// 處理被替換項的邏輯
}
// ... 其他邏輯 ...
}
}
}
Map
類型的字段 delegate
,它是需要適配的目標對象。RemovalListener
:創(chuàng)建一個內(nèi)部類 MyRemovalListener
實現(xiàn) RemovalListener
接口,并添加自定義的邏輯。MyCustomMap
類,實現(xiàn) Map
接口,并在構(gòu)造函數(shù)中接收一個 Map
對象。MyCustomMap
類中,實現(xiàn) Map
接口的方法,并將調(diào)用轉(zhuǎn)發(fā)到 delegate
對象。RemovalListener
:在創(chuàng)建緩存時,將 MyRemovalListener
注冊為緩存的移除監(jiān)聽器。MyRemovalListener
的 onRemoval
方法中,根據(jù)移除原因添加額外的邏輯。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ù),讓別人去卷吧。
更多建議: