Guava 源码中7种设计模式的实现分析

最近有小伙伴在 Guava 组件的使用上交流了一些问题,组件的使用很简单,优秀的人不仅仅在使用,学习 Guava 的源码设计是提高自己编程思想和能力的关键,跟着高手走,吃喝啥都有,跟着高手混,未来一定顺。哈哈,下面 V 哥从 Guava 源码中使用到的设计模式来详细介绍一下,希望能帮助你更好的理解设计模式的精髓,开整。

Guava 源码中使用到的设计模式主要包括以下几种:

  1. 建造者模式(Builder Pattern):在 Guava 中,CacheBuilder 类就是使用了建造者模式,它允许用户通过链式调用方法来设置缓存的各种参数,如初始容量、最大大小、过期时间等,最后通过 build() 方法构建并返回一个缓存实例。这种模式使得构建过程非常清晰,并且易于维护和扩展。

  2. 代理模式(Proxy Pattern):Guava 中的 ForwardingCollection 类是代理模式的一个应用,它提供了一个默认的代理实现,使得用户在实现自己的代理类时,可以只关注自己关心的方法,其他方法可以委托给被代理的对象来完成。

  3. 不可变模式(Immutable Pattern):Guava 提供了一系列不可变的集合类,如 ImmutableListImmutableSet 和 ImmutableMap 等。这些类确保了集合一旦创建,其内容就不能被修改。这种模式在多线程环境中非常有用,因为它可以避免并发修改的问题,提高代码的安全性和简洁性。

  4. 单例模式(Singleton Pattern):虽然在搜索结果中没有直接提及,但 Guava 的 LoadingCache 中的 CacheLoader 可以视为单例模式的一种应用,它确保了缓存加载器实例的唯一性。

  5. 装饰器模式(Decorator Pattern):Guava 中的 ForwardingObject 类可以看作是装饰器模式的一个基础实现,它允许向一个对象动态地添加额外的职责,而不需要修改它的类定义。

  6. 适配器模式(Adapter Pattern):类似于装饰器模式,Guava 中的 Forwarding 类也可以用于适配器模式,它提供了一个转换接口,使得一个类的实例能够作为另一个接口的实例使用。

  7. 观察者模式(Observer Pattern):Guava 的 RemovalListener 可以视为观察者模式的体现,它允许监听缓存项的移除事件,从而进行相应的处理。

这些设计模式在 Guava 框架中的应用中大大提高了代码的可读性、可维护性和扩展性。下面 V 哥来一一详细介绍。

1. 建造者模式(Builder Pattern)

CacheBuilder 类在 Guava 框架中是建造者模式的一个典型应用。以下是对 CacheBuilder 类中建造者模式实现的分析,包括其实现过程和步骤。

CacheBuilder 类的建造者模式实现:

  1. 定义建造者类CacheBuilder 类本身作为建造者,提供了一系列的方法来设置缓存的各种参数。

  2. 设置参数的方法CacheBuilder 提供了链式调用的方法来设置缓存的配置,例如 initialCapacitymaximumSizeexpireAfterWriteremovalListener 等。

  3. 返回缓存实例:设置完所有参数后,调用 build() 方法来返回一个根据这些参数构建的缓存实例。

代码示例:

为了更好的理解,我们来简化模拟一个CacheBuilder类的实现。

整理了这份Java面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处】即可免费获取

java

代码解读

复制代码

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。

  1. 无参构造函数CacheBuilder 类通常有一个无参的构造函数,用于创建建造者对象。

  2. 设置参数:通过公共的方法设置缓存的各种参数。每个设置方法都接受一个参数,并返回建造者对象自身的引用,允许链式调用。

  3. 参数校验:在 build() 方法中进行参数的有效性校验,确保缓存可以被正确构建。

  4. 构建缓存实例build() 方法根据前面设置的参数来创建并返回一个缓存实例。在 Guava 中,这个实例是 LocalCache 类型的对象。

  5. 链式调用:建造者模式的关键在于链式调用,使得设置参数的过程非常清晰和流畅。

  6. 灵活性和扩展性:如果未来需要添加新的配置参数,只需在 CacheBuilder 中添加新的方法,并在 build() 方法中进行相应的处理即可,无需修改其他调用 CacheBuilder 的代码。

建造者模式在 CacheBuilder 类中的应用,使得创建缓存实例的过程非常灵活和易于管理,同时保证了代码的清晰性和可维护性。

2. 代理模式(Proxy Pattern)

ForwardingCollection 类是 Guava 库中一个典型的代理模式的应用。以下是对 ForwardingCollection 类中代理模式实现的分析,包括实现过程和步骤。

ForwardingCollection 类的代理模式实现:

  1. 定义抽象方法ForwardingCollection 类提供了一个抽象方法 delegate(),它需要被子类实现以返回被包装的集合对象。

  2. 转发方法ForwardingCollection 类实现了 Collection 接口中的所有方法,并且在每个方法中,通过调用 delegate() 方法来获取被包装的集合对象,并将操作委托给它。

  3. 子类扩展:通过继承 ForwardingCollection 类并实现 delegate() 方法,用户可以在子类中添加额外的功能,而不需要修改原始的集合类。

代码示例:

还是上代码吧,这样更好理解一些(不要问我代码谁)

 

java

代码解读

复制代码

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; } // 可以添加额外的方法或者覆盖已有方法来添加日志功能 }

实现过程和步骤:

以下是实现过程和步骤的解析,一起来看一下。

  1. 定义委托方法:在 ForwardingCollection 中定义一个抽象的 delegate() 方法,强制要求子类实现它,以提供被代理的集合实例。

  2. 实现接口方法:为 Collection 接口中的每个方法提供一个默认实现,这些实现通过调用 delegate() 方法来转发操作。

  3. 创建子类:创建一个子类,比如 LoggingCollection,继承自 ForwardingCollection

  4. 实现委托方法:在子类中实现 delegate() 方法,返回实际的集合对象。

  5. 添加额外功能:在子类中添加额外的功能,例如在添加元素时打印日志。

  6. 覆盖默认方法:如果需要,可以覆盖 ForwardingCollection 中的默认方法来改变行为。

  7. 使用包装后的集合:使用 LoggingCollection 作为任何 Collection 接口的实现,它将委托所有操作给原始集合,并在操作时添加日志功能。

通过这种方式,ForwardingCollection 类提供了一种灵活的方法来为现有的集合对象添加额外的功能,而不需要修改原始的集合代码。这是代理模式的核心优势,即增加职责而不影响原有对象的结构,你 get 到了么。

3. 不可变模式(Immutable Pattern)

在 Guava 库中,ImmutableListImmutableSet 和 ImmutableMap 类实现了不可变模式(Immutable Pattern),确保了集合一旦创建,其状态(包含的元素)就不能被修改。以下是对这些类中不可变模式实现的分析,包括实现过程和步骤。

不可变模式的实现要点:

  1. 所有元素在构造时设置���不可变集合类通过构造函数接收所有初始化所需的元素,并在构造过程中构建最终的集合状态。

  2. 没有修改方法:不可变集合类不提供任何修改集合状态的方法,如 addremove 或 clear 等。

  3. 提供复制构造函数:为了创建包含新元素的集合,提供复制构造函数,它允许在现有集合的基础上添加或替换元素,并返回一个新的不可变集合实例。

  4. 使用内部静态工厂方法:提供静态工厂方法来创建集合实例,这些方法通常使用内部的构建器模式来收集元素。

  5. 使用 final 关键词:集合内部存储元素的数据结构被声明为 final,确保它们一旦被初始化就不能被重新赋值。

  6. 深度不可变性:如果集合包含其他对象的引用,确保这些对象也是不可变的,或者在添加到集合之前进行深拷贝。

ImmutableList类

以下是 ImmutableList 类的关键代码示例,展示了不可变模式的一些关键实现:

 

java

代码解读

复制代码

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 接口方法的实现 ... }

实现过程和步骤:

继续解释实现过程和步骤哈。

  1. 定义存储结构:定义一个 final 的数组或集合来存储元素。

  2. 私有构造函数:提供一个私有构造函数,它接受所有初始化所需的元素。

  3. 静态工厂方法:提供公共的静态工厂方法,用于创建不可变集合的实例。

  4. 元素复制:在构造函数中,对传入的元素数组进行复制,以确保输入的数组本身不会被修改。

  5. 禁止修改操作:不提供任何修改集合状态的公共方法。

  6. 提供访问方法:提供访问集合元素的方法,如 get

  7. 确保线程安全:由于集合状态不可变,天然线程安全,不需要额外的同步措施。

  8. 创建子类或变体:如果需要提供特定类型的不可变集合,可以创建子类或使用不同的静态工厂方法。

ImmutableSet类

ImmutableSet 类在 Guava 库中同样是不可变集合的一个实现,提供了一个不允许修改的 Set 集合。下面是 ImmutableSet 类关键实现的分析:

关键特性:

  1. 基于 CollectionsImmutableSet 通常是基于其他不可变集合,如 ImmutableList 或者另一个 ImmutableSet,来实现的。

  2. 构造函数私有化:为了防止状态被修改,ImmutableSet 的构造函数是私有的,只能通过静态工厂方法来创建实例。

  3. 使用 Hash Table:内部使用一个合适的数据结构(如 ImmutableMap 的键集)来存储元素,保证元素的唯一性。

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

  5. 返回新实例:对于看似修改操作的方法,如 add 或 remove,实际上会返回一个新的 ImmutableSet 实例。

  6. 迭代器安全:提供了安全的迭代器,不允许通过迭代器修改集合。

代码示例:

 

java

代码解读

复制代码

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 接口方法的实现 ... }

实现过程和步骤:

  1. 定义内部数据结构:定义一个内部的 ImmutableMap 来存储元素和对应的布尔值(通常为 true),因为 Set 需要唯一性。

  2. 私有构造函数:构造函数是私有的,只接受一个 ImmutableMap 对象。

  3. 静态工厂方法:提供公共的静态工厂方法,如 of 和 copyOf,用于创建 ImmutableSet 实例。

  4. 元素检查:实现 contains 方法,通过检查 map 是否包含元素来确定集合是否包含该对象。

  5. 禁止修改操作:不实现 addremove 等修改集合的方法,或者在这些方法中抛出 UnsupportedOperationException

  6. 返回新实例:如果需要执行看似修改操作的方法,创建并返回一个新的 ImmutableSet 实例。

  7. 提供迭代器:提供一个迭代器来遍历集合,但不允许修改集合。

  8. 确保不可变:确保所有内部数据结构都是不可变的,并且在任何对外提供访问的包装方法中,都不暴露可变视图。

ImmutableMap

ImmutableMap 类在 Guava 库中是不可变集合模式的一个重要实现,提供了一个不允许修改的 Map 集合。以下是 ImmutableMap 类关键实现的分析:

关键特性:

  1. 基于 HashMap 或 TreeMapImmutableMap 通常是基于一个不可变的 HashMap 或 TreeMap 来实现的。

  2. 构造函数私有化:为了防止状态被修改,ImmutableMap 的构造函数是私有的,只能通过静态工厂方法来创建实例。

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

  4. 返回新实例:对于看似修改操作的方法,如 put 或 remove,实际上会返回一个新的 ImmutableMap 实例。

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

  6. 使用 final 关键词:存储键值对的数据结构被声明为 final,确保它们一旦被初始化就不能被重新赋值。

代码示例:

 

java

代码解读

复制代码

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 } }; } }

实现过程和步骤:

  1. 定义内部数据结构:定义一个内部的数组或其他数据结构来存储键值对。

  2. 私有构造函数:构造函数是私有的,只接受一个包含所有键值对的数组。

  3. 静态工厂方法:提供公共的静态工厂方法,如 of 和 copyOf,用于创建 ImmutableMap 实例。

  4. 元素检查:实现 get 方法,通过遍历内部存储结构来查找并返回键对应的值。

  5. 禁止修改操作:不实现 putremove 等修改映射的方法,或者在这些方法中抛出 UnsupportedOperationException

  6. 返回新实例:如果需要执行看似修改操作的方法,创建并返回一个新的 ImmutableMap 实例。

  7. 提供迭代器:提供不可变的迭代器来遍历键值对、键或值。

  8. 确保不可变:确保所有内部数据结构都是不可变的,并且在任何对外提供访问的包装方法中,都不暴露可变视图。

  9. 实现视图方法:为 entrySet()keySet(), 和 values() 提供实现,确保返回的视图不提供修改原映射的能力。

划重点,一句话小结一下这3个类:

ImmutableListImmutableSetImmutableMap 类提供了一个安全、不可变的 Map 集合实现,适用于多线程环境和需要确保集合状态不会被改变的场景。

4. 单例模式(Singleton Pattern)

在 Guava 的 LoadingCache 中,CacheLoader 接口本身并不直接实现单例模式,但 CacheLoader 的实现可以是单例的。CacheLoader 接口用于定义加载缓存项的逻辑,当缓存未命中时,LoadingCache 将使用 CacheLoader 来加载数据。

然而,CacheLoader 的一个常见实现,MoreExecutors.listeningDecorator,实际上使用了单例模式。以下是对使用 MoreExecutors.listeningDecorator 作为 CacheLoader 的单例实现的分析:

单例模式的实现要点:

  1. 单例类:创建一个类,控制实例的创建,确保全局只存在一个实例。

  2. 私有构造函数:使构造函数私有,防止外部通过 new 来创建实例。

  3. 提供全局访问点:提供一个公共的静态方法,返回类的唯一实例。

  4. 延迟初始化:如果需要,可以在实例被使用时才创建它,实现延迟初始化。

代码示例:

 

java

代码解读

复制代码

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 的具体实现 } }

实现过程和步骤:

  1. 创建私有构造函数:在 MoreExecutors 类中创建一个私有构造函数,确保不能通过 new 关键字来创建实例。

  2. 提供静态方法:提供一个公共的静态方法 listeningDecorator,该方法接受一个 ExecutorService 参数。

  3. 检查参数类型:在 listeningDecorator 方法中,检查传入的 ExecutorService 是否已经实现了 ListeningExecutorService 接口。

  4. 返回现有实例或创建新实例:如果传入的 ExecutorService 已经是一个 ListeningExecutorService,则直接返回它;否则,创建一个新的 ListeningDecorator 实例。

  5. 实现单例逻辑ListeningDecorator 类作为内部静态类,确保了 MoreExecutors.listeningDecorator 方法每次调用时返回的都是同一个 ListeningDecorator 实例。

  6. 使用单例:在 LoadingCache 的构建过程中,使用 MoreExecutors.listeningDecorator 来获取单例的 ListeningExecutorService

  7. 线程安全:由于 ListeningDecorator 是一个静态类,它的实例化是线程安全的,并且在第一次创建后,后续的调用都会返回同一个实例。

通过这种方式,MoreExecutors.listeningDecorator 实现了单例模式,确保了无论何时何地调用该方法,都只会创建一个 ListeningExecutorService 的装饰实例。这在多线程环境中非常有用,因为它可以避免创建不必要的线程池实例,并确保所有线程共享同一个线程池。

5. 装饰器模式(Decorator Pattern)

在 Guava 库中,ForwardingObject 类是装饰器模式的一个应用。装饰器模式允许用户在不修改对象自身的基础上,向一个对象添加额外的职责。ForwardingObject 作为一个抽象类,提供了一个基础的装饰器实现,它将所有方法调用转发到被装饰对象上。

以下是 ForwardingObject 类中装饰器模式的实现分析,包括实现过程和步骤:

装饰器模式的实现要点:

  1. 定义抽象装饰器类:创建一个抽象类 ForwardingObject,它继承自 Forwarding 类,并实现 Object 接口。

  2. 定义被装饰对象的引用:在 ForwardingObject 类中定义一个类型为被装饰类的引用。

  3. 提供构造函数:提供一个构造函数,用于在创建装饰器实例时注入被装饰对象。

  4. 转发方法:实现 Object 类的方法,如 equalshashCode, 和 toString,将这些方法调用转发到被装饰对象。

  5. 提供抽象方法:定义一个抽象方法,如 delegate(),要求子类实现以返回被装饰对象。

  6. 子类扩展:通过继承 ForwardingObject 并实现抽象方法,用户可以在子类中添加额外的逻辑。

代码示例:

 

java

代码解读

复制代码

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(); } // 其他需要转发的方法... }

实现过程和步骤:

  1. 创建抽象装饰器类:定义 ForwardingObject 类,继承自 Forwarding 类,后者提供了默认的转发实现。

  2. 定义被装饰对象的引用:在 ForwardingObject 类中定义一个 final 引用 delegate,用于存储被装饰对象。

  3. 提供构造函数:提供一个构造函数,接受一个参数并赋值给 delegate 引用。

  4. 实现抽象方法:实现 delegate() 方法,返回被装饰对象的引用。

  5. 转发 Object 方法:重写 equalshashCode, 和 toString 方法,将调用转发到 delegate 对象。

  6. 扩展装饰器:用户创建一个继承自 ForwardingObject 的子类,并实现所需的额外逻辑。

  7. 使用装饰器:实例化装饰器子类,并向其构造函数中传入被装饰的对象。

  8. 保持接口一致性:确保装饰器类与被装饰类有相同的接口,这样客户端代码就可以透明地使用装饰器。

通过这种方式,ForwardingObject 类提供了一个灵活的装饰器模式实现,允许用户在运行时动态地添加额外的职责,而不需要修改原有的对象。这种模式在扩展功能、增加日志记录、缓存等场景下非常有用。

6. 适配器模式(Adapter Pattern)

在 Guava 库中,Forwarding 不是直接作为一个适配器模式的实现而存在,而是一个抽象基类,被用作简化装饰器模式、代理模式或适配器模式的实现。Forwarding 类通过委托机制,使得子类可以自定义委托给另一个对象的行为。

然而,Guava 中的 Forwarding 类似概念可以应用于适配器模式。适配器模式将一个类的接口转换成客户期望的另一个接口,使原本由于接口不兼容而不能一起工作的类可以一起工作。

以下是 Forwarding 类中适配器模式实现的分析:

适配器模式的实现要点:

  1. 定义抽象基类:创建一个抽象类 Forwarding,作为所有转发类的基类。

  2. 定义委托方法:在 Forwarding 类中定义一个抽象方法 delegate(),用于返回被包装或适配的对象。

  3. 实现默认行为:在 Forwarding 类中为所有继承自委托对象的方法提供一个默认的实现。

  4. 子类扩展:通过继承 Forwarding 类并实现 delegate() 方法,用户可以在子类中指定具体的被适配对象。

  5. 接口转换:在子类中,可以添加方法或覆盖现有方法,以转换或添加接口方法。

代码示例:

 

java

代码解读

复制代码

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(); } // 其他方法... }

实现过程和步骤:

  1. 定义抽象基类:创建 Forwarding 类,作为一个抽象基类提供转发逻辑。

  2. 定义委托方法:在 Forwarding 类中定义 delegate() 方法,它是一个抽象方法,由子类实现以返回实际被操作的对象。

  3. 实现默认行为:为 Object 类的方法如 equalshashCode 和 toString 提供默认实现,将调用转发到 delegate() 方法返回的对象。

  4. 扩展子类:创建一个子类,继承自 Forwarding 并实现 delegate() 方法,指定被适配的对象。

  5. 接口转换:在子类中实现需要适配的接口方法,将这些方法的调用转发到被适配对象的相应方法。

  6. 使用适配器:客户端代码通过 Forwarding 子类实例与被适配对象交互,从而实现接口转换。

通过这种方式,Forwarding 类的设计模式可以作为适配器模式的一个实现基础,允许开发者通过继承和委托机制,将一个类的接口转换成另一种形式,满足不同的接口需求。这在兼容旧接口、整合异构系统、或者提供额外功能时非常有用。

7. 观察者模式(Observer Pattern)

在 Guava 库中,RemovalListener 接口本身并不直接实现适配器模式,但它可以���作适配器模式的一部分。RemovalListener 是 Guava 缓存框架中的一个组件,用于监听缓存项的移除事件。当缓存项由于任何原因被移除时(例如,由于容量限制或超时),RemovalListener 可以接收通知并执行相应的操作。

以下是如何使用 RemovalListener 接口来实现适配器模式的分析:

适配器模式的实现要点:

  1. 定义客户端接口:定义客户端使用的接口,这通常是 Java 标准库中的接口,如 Map

  2. 定义目标接口:定义需要适配的目标接口,这可以是第三方库的接口,或者是自定义的接口。

  3. 创建适配器类:创建一个适配器类,实现客户端接口,并在内部持有目标接口的实例。

  4. 转发方法调用:在适配器类中,实现客户端接口的方法,并将调用转发到目标对象的相应方法。

  5. 添加额外的逻辑:在转发过程中,可以在适配器类中添加额外的逻辑,如事件监听。

代码示例:

 

java

代码解读

复制代码

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) { // 处理被替换项的逻辑 } // ... 其他逻辑 ... } } }

实现过程和步骤:

  1. 定义目标对象:创建一个 Map 类型的字段 delegate,它是需要适配的目标对象。

  2. 定义 RemovalListener:创建一个内部类 MyRemovalListener 实现 RemovalListener 接口,并添加自定义的逻辑。

  3. 创建适配器类:创建 MyCustomMap 类,实现 Map 接口,并在构造函数中接收一个 Map 对象。

  4. 转发方法调用:在 MyCustomMap 类中,实现 Map 接口的方法,并将调用转发到 delegate 对象。

  5. 注册 RemovalListener:在创建缓存时,将 MyRemovalListener 注册为缓存的移除监听器。

  6. 添加额外逻辑:在 MyRemovalListener 的 onRemoval 方法中,根据移除原因添加额外的逻辑。

  7. 使用适配器:客户端代码创建 MyCustomMap 实例,并像使用普通 Map 一样使用它,同时享受额外的移除事件监听功能。

通过这种方式,RemovalListener 可以作为适配器模式的一部分,使得 MyCustomMap 类在遵循 Map 接口的同时,增加了对缓存项移除事件的监听能力。这种模式在需要扩展现有类的功能时非常有用,特别是当无法直接修改现有类时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值