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 等都有对应实现

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

相关推荐
28岁青春痘老男孩14 小时前
JDK8+SpringBoot2.x 升级 JDK 17 + Spring Boot 3.x
java·spring boot
方璧14 小时前
限流的算法
java·开发语言
元Y亨H14 小时前
Nacos - 服务注册
java·微服务
曲莫终14 小时前
Java VarHandle全面详解:从入门到精通
java·开发语言
一心赚狗粮的宇叔14 小时前
中级软件开发工程师2025年度总结
java·大数据·oracle·c#
奋进的芋圆15 小时前
DataSyncManager 详解与 Spring Boot 迁移指南
java·spring boot·后端
计算机程序设计小李同学15 小时前
个人数据管理系统
java·vue.js·spring boot·后端·web安全
小途软件15 小时前
用于机器人电池电量预测的Sarsa强化学习混合集成方法
java·人工智能·pytorch·python·深度学习·语言模型
alonewolf_9915 小时前
Spring MVC启动与请求处理全流程解析:从DispatcherServlet到HandlerAdapter
java·spring·mvc
Echo娴15 小时前
Spring的开发步骤
java·后端·spring