Guava Forwarding系列类详解——装饰器模式实战

大家好,我是桦说编程。

本文通过 ForwardingList 深入解析 Guava 的 ForwardingXXX 系列类,掌握装饰器模式在集合扩展中的优雅实现。Fowarding系列类广义上涉及三种设计模式:装饰器模式、模版方法模式、委托模式。本文由我辅助AI编写。

问题背景

在实际开发中,我们经常需要对标准集合类进行功能增强:

  • 在 List 添加元素时自动记录日志
  • 对 Map 的 put 操作进行参数校验
  • 在 Set 的读写操作上添加性能埋点

传统做法是直接继承 ArrayList、HashMap,但这会遇到几个问题:

  1. 需要重写大量方法:List 接口有 30+ 个方法,逐个重写工作量大
  2. 容易遗漏:某些方法忘记重写会导致增强逻辑失效
  3. 方法委托关系复杂 :比如 addAll 内部可能调用 add,重写时需要理解内部实现

Guava 的 ForwardingXXX 系列正是为解决这类问题而设计。

核心原理

装饰器模式回顾

sql 复制代码
┌─────────────────────────────────────────────────┐
│                   <<interface>>                 │
│                       List                      │
├─────────────────────────────────────────────────┤
│  + add(E): boolean                              │
│  + get(int): E                                  │
│  + size(): int                                  │
│  ...                                            │
└─────────────────────────────────────────────────┘
                        ▲
                        │ implements
          ┌─────────────┴─────────────┐
          │                           │
┌─────────────────┐         ┌─────────────────────┐
│    ArrayList    │         │   ForwardingList    │
├─────────────────┤         ├─────────────────────┤
│ 真正的实现        │◄────────│ - delegate: List<E> │
└─────────────────┘   委托   │ + delegate(): List  │
                            │ + add(E): boolean   │
                            │   → delegate.add(e) │
                            └─────────────────────┘
                                      ▲
                                      │ extends
                            ┌─────────────────────┐
                            │   LoggingList<E>    │
                            ├─────────────────────┤
                            │ 只重写需要增强的方法 │
                            │ + add(E): boolean   │
                            │   → log + super.add │
                            └─────────────────────┘

ForwardingList 源码剖析

java 复制代码
@GwtCompatible
public abstract class ForwardingList<E> extends ForwardingCollection<E> implements List<E> {

    protected ForwardingList() {}

    // 子类必须实现:返回被装饰的 List
    @Override
    protected abstract List<E> delegate();

    // 所有方法都委托给 delegate()
    @Override
    public void add(int index, E element) {
        delegate().add(index, element);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> elements) {
        return delegate().addAll(index, elements);
    }

    @Override
    public E get(int index) {
        return delegate().get(index);
    }

    @Override
    public int indexOf(Object element) {
        return delegate().indexOf(element);
    }

    // ... 其他 List 方法同样委托

    // 标准方法实现:基于其他方法的默认实现
    protected boolean standardAdd(E element) {
        add(size(), element);
        return true;
    }

    protected boolean standardAddAll(int index, Iterable<? extends E> elements) {
        return Lists.addAllImpl(this, index, elements);
    }

    // ... 更多 standardXxx 方法
}

关键设计点

设计元素 说明
abstract delegate() 强制子类指定被装饰对象,延迟绑定
所有方法委托 默认行为透传,子类按需重写
standardXxx 方法 提供基于基本操作的默认实现,避免委托循环

standardXxx 方法的作用

这是 ForwardingXXX 的精华所在。考虑这个场景:

java 复制代码
// 错误示范:addAll 内部可能直接调用 delegate 的 addAll
public class LoggingList<E> extends ForwardingList<E> {
    @Override
    public boolean add(int index, E element) {
        log.info("Adding: {}", element);
        return delegate().add(index, element);
    }

    // addAll 没重写,直接委托给 delegate.addAll()
    // 结果:addAll 添加的元素不会被日志记录!
}

正确做法是使用 standardAddAll

java 复制代码
@Override
public boolean addAll(int index, Collection<? extends E> elements) {
    return standardAddAll(index, elements); // 内部循环调用 this.add()
}

实战示例

示例1:带日志的 List

java 复制代码
public class LoggingList<E> extends ForwardingList<E> {
    private static final Logger log = LoggerFactory.getLogger(LoggingList.class);
    private final List<E> delegate;

    public LoggingList(List<E> delegate) {
        this.delegate = delegate;
    }

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

    @Override
    public boolean add(E element) {
        log.info("[ADD] element={}", element);
        return standardAdd(element);
    }

    @Override
    public void add(int index, E element) {
        log.info("[ADD] index={}, element={}", index, element);
        delegate().add(index, element);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> elements) {
        log.info("[ADD_ALL] index={}, size={}", index, elements.size());
        return standardAddAll(index, elements); // 会逐个调用 add,每个都有日志
    }

    @Override
    public E remove(int index) {
        E removed = delegate().remove(index);
        log.info("[REMOVE] index={}, element={}", index, removed);
        return removed;
    }
}

示例2:带容量限制的 List

java 复制代码
public class BoundedList<E> extends ForwardingList<E> {
    private final List<E> delegate;
    private final int maxSize;

    public BoundedList(List<E> delegate, int maxSize) {
        this.delegate = delegate;
        this.maxSize = maxSize;
    }

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

    private void checkCapacity(int additionalElements) {
        if (size() + additionalElements > maxSize) {
            throw new IllegalStateException(
                "Exceeds max size: " + maxSize + ", current: " + size());
        }
    }

    @Override
    public boolean add(E element) {
        checkCapacity(1);
        return standardAdd(element);
    }

    @Override
    public void add(int index, E element) {
        checkCapacity(1);
        delegate().add(index, element);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> elements) {
        checkCapacity(elements.size());
        return delegate().addAll(index, elements);
    }
}

示例3:性能埋点 List

java 复制代码
public class MetricsList<E> extends ForwardingList<E> {
    private final List<E> delegate;
    private final AtomicLong readCount = new AtomicLong();
    private final AtomicLong writeCount = new AtomicLong();

    public MetricsList(List<E> delegate) {
        this.delegate = delegate;
    }

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

    @Override
    public E get(int index) {
        readCount.incrementAndGet();
        return delegate().get(index);
    }

    @Override
    public boolean add(E element) {
        writeCount.incrementAndGet();
        return standardAdd(element);
    }

    @Override
    public void add(int index, E element) {
        writeCount.incrementAndGet();
        delegate().add(index, element);
    }

    public long getReadCount() {
        return readCount.get();
    }

    public long getWriteCount() {
        return writeCount.get();
    }
}

ForwardingXXX 家族

Guava 为常用集合都提供了 Forwarding 包装类:

类名 对应接口 典型应用场景
ForwardingList List 有序集合增强
ForwardingSet Set 去重集合增强
ForwardingMap Map K-V 映射增强
ForwardingQueue Queue 队列增强
ForwardingMultimap Multimap 一对多映射增强
ForwardingTable Table 二维表增强
ForwardingIterator Iterator 迭代器增强
ForwardingConcurrentMap ConcurrentMap 并发 Map 增强

与直接继承对比

方面 直接继承 ArrayList ForwardingList
代码量 重写所有需要增强的方法 只重写需要增强的方法
灵活性 绑定特定实现 可装饰任意 List 实现
组合能力 单一继承 可多层装饰叠加
实现替换 需修改代码 构造时注入即可

装饰器叠加示例

java 复制代码
// 一个 List 同时具备日志 + 容量限制 + 埋点
List<String> base = new ArrayList<>();
List<String> bounded = new BoundedList<>(base, 100);
List<String> logged = new LoggingList<>(bounded);
List<String> metered = new MetricsList<>(logged);

// 调用顺序:metered → logged → bounded → base
metered.add("item");

总结

  • ForwardingXXX 是装饰器模式的标准实现 :通过 delegate() 委托,只重写需要增强的方法
  • standardXxx 方法避免委托循环:当增强逻辑依赖基础操作时,使用 standard 方法确保调用链正确
  • 支持多层装饰叠加:日志、校验、埋点等可以自由组合
  • Guava 提供全系列 Forwarding 类:List、Set、Map、Queue、Multimap、Table 等都有对应实现

如果这篇文章对你有帮助,欢迎关注我,持续分享高质量技术干货,助你更快提升编程能力。

相关推荐
AI人工智能+电脑小能手6 分钟前
大白话说Java设计模式-08-建造者模式(业务实战篇)
java·设计模式·建造者模式·架构设计·对象构建
xbgRS17 分钟前
java中的线程
java
worxfr21 分钟前
Go 并发控制:从 Channel 方向约束到实战模式
开发语言·后端·golang
拾陆楼31 分钟前
PT: DMSA辅助调tree报告前后级余量脚本
后端·学习
今天的砖头有点烫手啊31 分钟前
接口太慢?Spring Boot 缓存体系 @Cacheable 全链路拆解
spring boot·后端·缓存
ChaHae-In1 小时前
MyBatis入门操作
java·mybatis
xcLeigh1 小时前
Go入门:短变量声明的陷阱与最佳实践
java·redis·golang·教程·变量
青山木1 小时前
Hot 100 --- 最小栈
java·数据结构·算法·leetcode
疯狂打码的少年1 小时前
【数据结构】栈的应用:表达式求值(后缀表达式)
java·数据结构·笔记·算法
szephyr1 小时前
腾讯云 ADP 智能体的 Skills 版本回滚总是回到旧配置,是缓存没清还是版本管理没开?
java·缓存·腾讯云