最近有小伙伴在 Guava 组件的使用上交流了一些问题,组件的使用很简单,优秀的人不仅仅在使用,学习 Guava 的源码设计是提高自己编程思想和能力的关键,跟着高手走,吃喝啥都有,跟着高手混,未来一定顺。哈哈,下面 V 哥从 Guava 源码中使用到的设计模式来详细介绍一下,希望能帮助你更好的理解设计模式的精髓,开整。
Guava 源码中使用到的设计模式主要包括以下几种:
- 建造者模式(Builder Pattern):在 Guava 中,
CacheBuilder
类就是使用了建造者模式,它允许用户通过链式调用方法来设置缓存的各种参数,如初始容量、最大大小、过期时间等,最后通过build()
方法构建并返回一个缓存实例。这种模式使得构建过程非常清晰,并且易于维护和扩展。 - 代理模式(Proxy Pattern):Guava 中的
ForwardingCollection
类是代理模式的一个应用,它提供了一个默认的代理实现,使得用户在实现自己的代理类时,可以只关注自己关心的方法,其他方法可以委托给被代理的对象来完成。 - 不可变模式(Immutable Pattern):Guava 提供了一系列不可变的集合类,如
ImmutableList
、ImmutableSet
和ImmutableMap
等。这些类确保了集合一旦创建,其内容就不能被修改。这种模式在多线程环境中非常有用,因为它可以避免并发修改的问题,提高代码的安全性和简洁性。 - 单例模式(Singleton Pattern):虽然在搜索结果中没有直接提及,但 Guava 的
LoadingCache
中的CacheLoader
可以视为单例模式的一种应用,它确保了缓存加载器实例的唯一性。 - 装饰器模式(Decorator Pattern):Guava 中的
ForwardingObject
类可以看作是装饰器模式的一个基础实现,它允许向一个对象动态地添加额外的职责,而不需要修改它的类定义。 - 适配器模式(Adapter Pattern):类似于装饰器模式,Guava 中的
Forwarding
类也可以用于适配器模式,它提供了一个转换接口,使得一个类的实例能够作为另一个接口的实例使用。 - 观察者模式(Observer Pattern):Guava 的
RemovalListener
可以视为观察者模式的体现,它允许监听缓存项的移除事件,从而进行相应的处理。
这些设计模式在 Guava 框架中的应用中大大提高了代码的可读性、可维护性和扩展性。下面 V 哥来一一详细介绍。
1. 建造者模式(Builder Pattern)
CacheBuilder
类在 Guava 框架中是建造者模式的一个典型应用。以下是对 CacheBuilder
类中建造者模式实现的分析,包括其实现过程和步骤。
CacheBuilder 类的建造者模式实现:
- 定义建造者类:
CacheBuilder
类本身作为建造者,提供了一系列的方法来设置缓存的各种参数。 - 设置参数的方法:
CacheBuilder
提供了链式调用的方法来设置缓存的配置,例如initialCapacity
、maximumSize
、expireAfterWrite
、removalListener
等。 - 返回缓存实例:设置完所有参数后,调用
build()
方法来返回一个根据这些参数构建的缓存实例。
代码示例:
为了更好的理解,我们来简化模拟一个CacheBuilder
类的实现。
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; // 链式调用
}
public CacheBuilder<K, V> maximumSize(long maxSize) {
this.maximumSize = maxSize;
return this; // 链式调用
}
public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
this.expireAfterWriteNanos = unit.toNanos(duration);
return this; // 链式调用
}
// ... 省略其他设置方法 ...
public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
// 检查参数有效性
if (concurrencyLevel > MAX_SEGMENTS) {
throw new IllegalArgumentException("concurrencyLevel cannot be greater than " + MAX_SEGMENTS);
}
// 构建并返回缓存实例
return new LocalCache<K, V>(this);
}
// ... 省略其他方法 ...
}
实现过程和步骤:
以上代码的实现地这程和步骤是这样滴,一起来 lock lock。
- 无参构造函数:
CacheBuilder
类通常有一个无参的构造函数,用于创建建造者对象。 - 设置参数:通过公共的方法设置缓存的各种参数。每个设置方法都接受一个参数,并返回建造者对象自身的引用,允许链式调用。
- 参数校验:在
build()
方法中进行参数的有效性校验,确保缓存可以被正确构建。 - 构建缓存实例:
build()
方法根据前面设置的参数来创建并返回一个缓存实例。在 Guava 中,这个实例是LocalCache
类型的对象。 - 链式调用:建造者模式的关键在于链式调用,使得设置参数的过程非常清晰和流畅。
- 灵活性和扩展性:如果未来需要添加新的配置参数,只需在
CacheBuilder
中添加新的方法,并在build()
方法中进行相应的处理即可,无需修改其他调用CacheBuilder
的代码。
建造者模式在 CacheBuilder
类中的应用,使得创建缓存实例的过程非常灵活和易于管理,同时保证了代码的清晰性和可维护性。
2. 代理模式(Proxy Pattern)
ForwardingCollection
类是 Guava 库中一个典型的代理模式的应用。以下是对 ForwardingCollection
类中代理模式实现的分析,包括实现过程和步骤。
ForwardingCollection 类的代理模式实现:
- 定义抽象方法:
ForwardingCollection
类提供了一个抽象方法delegate()
,它需要被子类实现以返回被包装的集合对象。 - 转发方法:
ForwardingCollection
类实现了Collection
接口中的所有方法,并且在每个方法中,通过调用delegate()
方法来获取被包装的集合对象,并将操作委托给它。 - 子类扩展:通过继承
ForwardingCollection
类并实现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 接口方法的默认实现 ...
// 可以覆盖的方法,例如添加额外日志的 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;
}
// 可以添加额外的方法或者覆盖已有方法来添加日志功能
}
实现过程和步骤:
以下是实现过程和步骤的解析,一起来看一下。
- 定义委托方法:在
ForwardingCollection
中定义一个抽象的delegate()
方法,强制要求子类实现它,以提供被代理的集合实例。 - 实现接口方法:为
Collection
接口中的每个方法提供一个默认实现,这些实现通过调用delegate()
方法来转发操作。 - 创建子类:创建一个子类,比如
LoggingCollection
,继承自ForwardingCollection
。 - 实现委托方法:在子类中实现
delegate()
方法,返回实际的集合对象。 - 添加额外功能:在子类中添加额外的功能,例如在添加元素时打印日志。
- 覆盖默认方法:如果需要,可以覆盖
ForwardingCollection
中的默认方法来改变行为。 - 使用包装后的集合:使用
LoggingCollection
作为任何Collection
接口的实现,它将委托所有操作给原始集合,并在操作时添加日志功能。
通过这种方式,ForwardingCollection
类提供了一种灵活的方法来为现有的集合对象添加额外的功能,而不需要修改原始的集合代码。这是代理模式的核心优势,即增加职责而不影响原有对象的结构,你 get 到了么。
3. 不可变模式(Immutable Pattern)
在 Guava 库中,ImmutableList
、ImmutableSet
和 ImmutableMap
类实现了不可变模式(Immutable Pattern),确保了集合一旦创建,其状态(包含的元素)就不能被修改。以下是对这些类中不可变模式实现的分析,包括实现过程和步骤。
不可变模式的实现要点:
- 所有元素在构造时设置:不可变集合类通过构造函数接收所有初始化所需的元素,并在构造过程中构建最终的集合状态。
- 没有修改方法:不可变集合类不提供任何修改集合状态的方法,如
add
、remove
或clear
等。 - 提供复制构造函数:为了创建包含新元素的集合,提供复制构造函数,它允许在现有集合的基础上添加或替换元素,并返回一个新的不可变集合实例。
- 使用内部静态工厂方法:提供静态工厂方法来创建集合实例,这些方法通常使用内部的构建器模式来收集元素。
- 使用 final 关键词:集合内部存储元素的数据结构被声明为 final,确保它们一旦被初始化就不能被重新赋值。
- 深度不可变性:如果集合包含其他对象的引用,确保这些对象也是不可变的,或者在添加到集合之前进行深拷贝。
ImmutableList类
以下是 ImmutableList
类的关键代码示例,展示了不可变模式的一些关键实现:
public final class ImmutableList<E> extends ImmutableCollection<E> implements List<E> {
private final transient Object[] array; // 存储元素的数组
// 私有构造函数,通过内部的 Builder 类来设置元素
private ImmutableList(Object[] array) {
this.array = array;
}
// 公共静态工厂方法,用于创建 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));
}
// 复制数组的工具方法,确保输入数组的不可变性
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 接口方法的实现 ...
}
实现过程和步骤:
继续解释实现过程和步骤哈。
- 定义存储结构:定义一个 final 的数组或集合来存储元素。
- 私有构造函数:提供一个私有构造函数,它接受所有初始化所需的元素。
- 静态工厂方法:提供公共的静态工厂方法,用于创建不可变集合的实例。
- 元素复制:在构造函数中,对传入的元素数组进行复制,以确保输入的数组本身不会被修改。
- 禁止修改操作:不提供任何修改集合状态的公共方法。
- 提供访问方法:提供访问集合元素的方法,如
get
。 - 确保线程安全:由于集合状态不可变,天然线程安全,不需要额外的同步措施。
- 创建子类或变体:如果需要提供特定类型的不可变集合,可以创建子类或使用不同的静态工厂方法。
ImmutableSet类
ImmutableSet
类在 Guava 库中同样是不可变集合的一个实现,提供了一个不允许修改的 Set 集合。下面是 ImmutableSet
类关键实现的分析:
关键特性:
- 基于 Collections:
ImmutableSet
通常是基于其他不可变集合,如ImmutableList
或者另一个ImmutableSet
,来实现的。 - 构造函数私有化:为了防止状态被修改,
ImmutableSet
的构造函数是私有的,只能通过静态工厂方法来创建实例。 - 使用 Hash Table:内部使用一个合适的数据结构(如
ImmutableMap
的键集)来存储元素,保证元素的唯一性。 - 不提供修改操作:不提供任何添加、删除或清空集合的方法。
- 返回新实例:对于看似修改操作的方法,如
add
或remove
,实际上会返回一个新的ImmutableSet
实例。 - 迭代器安全:提供了安全的迭代器,不允许通过迭代器修改集合。
代码示例:
public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements Set<E> {
// 内部使用 ImmutableMap 来存储元素
private transient ImmutableMap<E, Boolean> map;
protected ImmutableSet(ImmutableMap<E, Boolean> map) {
this.map = map;
}
// 公共静态工厂方法,用于创建 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 接口方法的实现 ...
}
实现过程和步骤:
- 定义内部数据结构:定义一个内部的
ImmutableMap
来存储元素和对应的布尔值(通常为true
),因为 Set 需要唯一性。 - 私有构造函数:构造函数是私有的,只接受一个
ImmutableMap
对象。 - 静态工厂方法:提供公共的静态工厂方法,如
of
和copyOf
,用于创建ImmutableSet
实例。 - 元素检查:实现
contains
方法,通过检查map
是否包含元素来确定集合是否包含该对象。 - 禁止修改操作:不实现
add
、remove
等修改集合的方法,或者在这些方法中抛出UnsupportedOperationException
。 - 返回新实例:如果需要执行看似修改操作的方法,创建并返回一个新的
ImmutableSet
实例。 - 提供迭代器:提供一个迭代器来遍历集合,但不允许修改集合。
- 确保不可变:确保所有内部数据结构都是不可变的,并且在任何对外提供访问的包装方法中,都不暴露可变视图。
ImmutableMap
类
ImmutableMap
类在 Guava 库中是不可变集合模式的一个重要实现,提供了一个不允许修改的 Map 集合。以下是 ImmutableMap
类关键实现的分析:
关键特性:
- 基于 HashMap 或 TreeMap:
ImmutableMap
通常是基于一个不可变的HashMap
或TreeMap
来实现的。 - 构造函数私有化:为了防止状态被修改,
ImmutableMap
的构造函数是私有的,只能通过静态工厂方法来创建实例。 - 不提供修改操作:不提供任何添加、删除或清空映射的方法。
- 返回新实例:对于看似修改操作的方法,如
put
或remove
,实际上会返回一个新的ImmutableMap
实例。 - 迭代器安全:提供了安全的
entrySet()
,keySet()
, 和values()
迭代器,不允许通过迭代器修改集合。 - 使用 final 关键词:存储键值对的数据结构被声明为 final,确保它们一旦被初始化就不能被重新赋值。
代码示例:
public abstract class ImmutableMap<K, V> implements Map<K, V> {
// 存储键值对的数组或其它数据结构
private final transient Entry<K, V>[] entries;
protected ImmutableMap(Entry<K, V>[] entries) {
this.entries = entries;
}
// 公共静态工厂方法,用于创建 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) {
// 根据键查找值的逻辑
}
// 返回新实例而不是修改当前映射
public ImmutableMap<K, V> putAll(Map<? extends K, ? extends V> map) {
throw new UnsupportedOperationException();
}
// ... 省略其他 Map 接口方法的实现 ...
// 提供迭代器
public Iterator<Entry<K, V>> entryIterator() {
return new UnmodifiableIterator<>() {
public Entry<K, V> next() {
// 返回不可变的 Entry
}
};
}
}
实现过程和步骤:
- 定义内部数据结构:定义一个内部的数组或其他数据结构来存储键值对。
- 私有构造函数:构造函数是私有的,只接受一个包含所有键值对的数组。
- 静态工厂方法:提供公共的静态工厂方法,如
of
和copyOf
,用于创建ImmutableMap
实例。 - 元素检查:实现
get
方法,通过遍历内部存储结构来查找并返回键对应的值。 - 禁止修改操作:不实现
put
、remove
等修改映射的方法,或者在这些方法中抛出UnsupportedOperationException
。 - 返回新实例:如果需要执行看似修改操作的方法,创建并返回一个新的
ImmutableMap
实例。 - 提供迭代器:提供不可变的迭代器来遍历键值对、键或值。
- 确保不可变:确保所有内部数据结构都是不可变的,并且在任何对外提供访问的包装方法中,都不暴露可变视图。
- 实现视图方法:为
entrySet()
,keySet()
, 和values()
提供实现,确保返回的视图不提供修改原映射的能力。
划重点,一句话小结一下这3个类:
ImmutableList
、ImmutableSet
、ImmutableMap
类提供了一个安全、不可变的 Map 集合实现,适用于多线程环境和需要确保集合状态不会被改变的场景。
4. 单例模式(Singleton Pattern)
在 Guava 的 LoadingCache
中,CacheLoader
接口本身并不直接实现单例模式,但 CacheLoader
的实现可以是单例的。CacheLoader
接口用于定义加载缓存项的逻辑,当缓存未命中时,LoadingCache
将使用 CacheLoader
来加载数据。
然而,CacheLoader
的一个常见实现,MoreExecutors.listeningDecorator
,实际上使用了单例模式。以下是对使用 MoreExecutors.listeningDecorator
作为 CacheLoader
的单例实现的分析:
单例模式的实现要点:
- 单例类:创建一个类,控制实例的创建,确保全局只存在一个实例。
- 私有构造函数:使构造函数私有,防止外部通过
new
来创建实例。 - 提供全局访问点:提供一个公共的静态方法,返回类的唯一实例。
- 延迟初始化:如果需要,可以在实例被使用时才创建它,实现延迟初始化。
代码示例:
public final class MoreExecutors {
private MoreExecutors() {
// 私有构造函数,防止实例化
}
public static ListeningExecutorService listeningDecorator(ExecutorService executor) {
if (executor instanceof ListeningExecutorService) {
return (ListeningExecutorService) executor;
}
return new ListeningDecorator(executor);
}
private static class ListeningDecorator extends AbstractListeningExecutorService {
// ListeningDecorator 的具体实现
}
}
实现过程和步骤:
- 创建私有构造函数:在
MoreExecutors
类中创建一个私有构造函数,确保不能通过new
关键字来创建实例。 - 提供静态方法:提供一个公共的静态方法
listeningDecorator
,该方法接受一个ExecutorService
参数。 - 检查参数类型:在
listeningDecorator
方法中,检查传入的ExecutorService
是否已经实现了ListeningExecutorService
接口。 - 返回现有实例或创建新实例:如果传入的
ExecutorService
已经是一个ListeningExecutorService
,则直接返回它;否则,创建一个新的ListeningDecorator
实例。 - 实现单例逻辑:
ListeningDecorator
类作为内部静态类,确保了MoreExecutors.listeningDecorator
方法每次调用时返回的都是同一个ListeningDecorator
实例。 - 使用单例:在
LoadingCache
的构建过程中,使用MoreExecutors.listeningDecorator
来获取单例的ListeningExecutorService
。 - 线程安全:由于
ListeningDecorator
是一个静态类,它的实例化是线程安全的,并且在第一次创建后,后续的调用都会返回同一个实例。
通过这种方式,MoreExecutors.listeningDecorator
实现了单例模式,确保了无论何时何地调用该方法,都只会创建一个 ListeningExecutorService
的装饰实例。这在多线程环境中非常有用,因为它可以避免创建不必要的线程池实例,并确保所有线程共享同一个线程池。
5. 装饰器模式(Decorator Pattern)
在 Guava 库中,ForwardingObject
类是装饰器模式的一个应用。装饰器模式允许用户在不修改对象自身的基础上,向一个对象添加额外的职责。ForwardingObject
作为一个抽象类,提供了一个基础的装饰器实现,它将所有方法调用转发到被装饰对象上。
以下是 ForwardingObject
类中装饰器模式的实现分析,包括实现过程和步骤:
装饰器模式的实现要点:
- 定义抽象装饰器类:创建一个抽象类
ForwardingObject
,它继承自Forwarding
类,并实现Object
接口。 - 定义被装饰对象的引用:在
ForwardingObject
类中定义一个类型为被装饰类的引用。 - 提供构造函数:提供一个构造函数,用于在创建装饰器实例时注入被装饰对象。
- 转发方法:实现
Object
类的方法,如equals
,hashCode
, 和toString
,将这些方法调用转发到被装饰对象。 - 提供抽象方法:定义一个抽象方法,如
delegate()
,要求子类实现以返回被装饰对象。 - 子类扩展:通过继承
ForwardingObject
并实现抽象方法,用户可以在子类中添加额外的逻辑。
代码示例:
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();
}
// 其他需要转发的方法...
}
实现过程和步骤:
- 创建抽象装饰器类:定义
ForwardingObject
类,继承自Forwarding
类,后者提供了默认的转发实现。 - 定义被装饰对象的引用:在
ForwardingObject
类中定义一个final
引用delegate
,用于存储被装饰对象。 - 提供构造函数:提供一个构造函数,接受一个参数并赋值给
delegate
引用。 - 实现抽象方法:实现
delegate()
方法,返回被装饰对象的引用。 - 转发
Object
方法:重写equals
,hashCode
, 和toString
方法,将调用转发到delegate
对象。 - 扩展装饰器:用户创建一个继承自
ForwardingObject
的子类,并实现所需的额外逻辑。 - 使用装饰器:实例化装饰器子类,并向其构造函数中传入被装饰的对象。
- 保持接口一致性:确保装饰器类与被装饰类有相同的接口,这样客户端代码就可以透明地使用装饰器。
通过这种方式,ForwardingObject
类提供了一个灵活的装饰器模式实现,允许用户在运行时动态地添加额外的职责,而不需要修改原有的对象。这种模式在扩展功能、增加日志记录、缓存等场景下非常有用。
6. 适配器模式(Adapter Pattern)
在 Guava 库中,Forwarding
不是直接作为一个适配器模式的实现而存在,而是一个抽象基类,被用作简化装饰器模式、代理模式或适配器模式的实现。Forwarding
类通过委托机制,使得子类可以自定义委托给另一个对象的行为。
然而,Guava 中的 Forwarding
类似概念可以应用于适配器模式。适配器模式将一个类的接口转换成客户期望的另一个接口,使原本由于接口不兼容而不能一起工作的类可以一起工作。
以下是 Forwarding
类中适配器模式实现的分析:
适配器模式的实现要点:
- 定义抽象基类:创建一个抽象类
Forwarding
,作为所有转发类的基类。 - 定义委托方法:在
Forwarding
类中定义一个抽象方法delegate()
,用于返回被包装或适配的对象。 - 实现默认行为:在
Forwarding
类中为所有继承自委托对象的方法提供一个默认的实现。 - 子类扩展:通过继承
Forwarding
类并实现delegate()
方法,用户可以在子类中指定具体的被适配对象。 - 接口转换:在子类中,可以添加方法或覆盖现有方法,以转换或添加接口方法。
代码示例:
public abstract class Forwarding {
// 抽象方法,由子类实现,返回被委托的对象
protected abstract Object delegate();
// 示例:转发 equals 方法
@Override
public boolean equals(Object obj) {
return delegate().equals(obj);
}
// 示例:转发 hashCode 方法
@Override
public int hashCode() {
return delegate().hashCode();
}
// 示例:转发 toString 方法
@Override
public String toString() {
return delegate().toString();
}
// 其他方法...
}
实现过程和步骤:
- 定义抽象基类:创建
Forwarding
类,作为一个抽象基类提供转发逻辑。 - 定义委托方法:在
Forwarding
类中定义delegate()
方法,它是一个抽象方法,由子类实现以返回实际被操作的对象。 - 实现默认行为:为
Object
类的方法如equals
、hashCode
和toString
提供默认实现,将调用转发到delegate()
方法返回的对象。 - 扩展子类:创建一个子类,继承自
Forwarding
并实现delegate()
方法,指定被适配的对象。 - 接口转换:在子类中实现需要适配的接口方法,将这些方法的调用转发到被适配对象的相应方法。
- 使用适配器:客户端代码通过
Forwarding
子类实例与被适配对象交互,从而实现接口转换。
通过这种方式,Forwarding
类的设计模式可以作为适配器模式的一个实现基础,允许开发者通过继承和委托机制,将一个类的接口转换成另一种形式,满足不同的接口需求。这在兼容旧接口、整合异构系统、或者提供额外功能时非常有用。
7. 观察者模式(Observer Pattern)
在 Guava 库中,RemovalListener
接口本身并不直接实现适配器模式,但它可以用作适配器模式的一部分。RemovalListener
是 Guava 缓存框架中的一个组件,用于监听缓存项的移除事件。当缓存项由于任何原因被移除时(例如,由于容量限制或超时),RemovalListener
可以接收通知并执行相应的操作。
以下是如何使用 RemovalListener
接口来实现适配器模式的分析:
适配器模式的实现要点:
- 定义客户端接口:定义客户端使用的接口,这通常是 Java 标准库中的接口,如
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 的方法调用转发到 delegate
@Override
public V put(K key, V value) {
// 在添加新值之前,可能需要执行一些逻辑
return delegate.put(key, value);
}
// ... 其他 Map 方法的实现 ...
// 自定义的 RemovalListener 实现
private class MyRemovalListener implements RemovalListener<K, V> {
@Override
public void onRemoval(RemovalNotification<K, V> notification) {
// 当缓存项被移除时,执行额外的逻辑
if (notification.getCause() == RemovalCause.REPLACED) {
// 处理被替换项的逻辑
}
// ... 其他逻辑 ...
}
}
}
实现过程和步骤:
- 定义目标对象:创建一个
Map
类型的字段delegate
,它是需要适配的目标对象。 - 定义
RemovalListener
:创建一个内部类MyRemovalListener
实现RemovalListener
接口,并添加自定义的逻辑。 - 创建适配器类:创建
MyCustomMap
类,实现Map
接口,并在构造函数中接收一个Map
对象。 - 转发方法调用:在
MyCustomMap
类中,实现Map
接口的方法,并将调用转发到delegate
对象。 - 注册
RemovalListener
:在创建缓存时,将MyRemovalListener
注册为缓存的移除监听器。 - 添加额外逻辑:在
MyRemovalListener
的onRemoval
方法中,根据移除原因添加额外的逻辑。 - 使用适配器:客户端代码创建
MyCustomMap
实例,并像使用普通Map
一样使用它,同时享受额外的移除事件监听功能。
通过这种方式,RemovalListener
可以作为适配器模式的一部分,使得 MyCustomMap
类在遵循 Map
接口的同时,增加了对缓存项移除事件的监听能力。这种模式在需要扩展现有类的功能时非常有用,特别是当无法直接修改现有类时。
最后
以上是 V 哥在学习 Guava 源码中总结的7个设计模式的实现分析,欢迎关注威哥爱编程,做自己的技术,让别人去卷吧。