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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: V 哥在学习 Guava 源码中总结的7个设计模式的实现分析,欢迎关注威哥爱编程,做自己的技术,让别人去卷吧。

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

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

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

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

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

  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类的实现。

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() 方法,用户可以在子类中添加额外的功能,而不需要修改原始的集合类。

代码示例:

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

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

不可变模式的实现要点:

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

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

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

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

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

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

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

实现过程和步骤:

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

  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. 返回新实例:对于看似修改操作的方法,如 addremove,实际上会返回一个新的 ImmutableSet 实例。

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

代码示例:

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. 静态工厂方法:提供公共的静态工厂方法,如 ofcopyOf,用于创建 ImmutableSet 实例。

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

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

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

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

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

ImmutableMap

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

关键特性:

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

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

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

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

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

  6. 使用 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
            }
        };
    }
}

实现过程和步骤:

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

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

  3. 静态工厂方法:提供公共的静态工厂方法,如 ofcopyOf,用于创建 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. 延迟初始化:如果需要,可以在实例被使用时才创建它,实现延迟初始化。

代码示例:

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 类的方法,如 equals, hashCode, 和 toString,将这些方法调用转发到被装饰对象。

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

  6. 子类扩展:通过继承 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();
    }

    // 其他需要转发的方法...
}

实现过程和步骤:

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

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

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

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

  5. 转发 Object 方法:重写 equals, hashCode, 和 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. 接口转换:在子类中,可以添加方法或覆盖现有方法,以转换或添加接口方法。

代码示例:

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 类的方法如 equalshashCodetoString 提供默认实现,将调用转发到 delegate() 方法返回的对象。

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

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

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

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

7. 观察者模式(Observer Pattern)

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

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

适配器模式的实现要点:

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

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

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

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

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

代码示例:

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. 添加额外逻辑:在 MyRemovalListeneronRemoval 方法中,根据移除原因添加额外的逻辑。

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

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

最后

以上是 V 哥在学习 Guava 源码中总结的7个设计模式的实现分析,欢迎关注威哥爱编程,做自己的技术,让别人去卷吧。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
14天前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
29天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
232 37
|
19天前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
4月前
|
设计模式 Java 开发者
Head First设计模式详解与应用场景分析
Head First设计模式详解与应用场景分析
|
3月前
|
设计模式 安全 Java
Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
86 0
|
3月前
|
设计模式 Java 开发者
工厂设计模式的实现与应用场景分析
工厂设计模式的实现与应用场景分析
|
5月前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
219 3
|
5月前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~结构型]不兼容结构的协调——适配器模式
[设计模式Java实现附plantuml源码~结构型]不兼容结构的协调——适配器模式
|
5月前
|
设计模式 安全 Java
[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式
[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式
|
5月前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~结构型] 扩展系统功能——装饰模式
[设计模式Java实现附plantuml源码~结构型] 扩展系统功能——装饰模式