对象持有另一个对象,再把调用转交过去,外形都像“包一层”。判断模式时更重要的是包装目的:增加行为、转换接口、简化入口,还是控制访问。

这些是设计模式学习整理,概念与类比可结合《Head First 设计模式》阅读,不把模式名称当成某个项目的性能或安全保证。

装饰:保持契约,组合附加行为

汽车例子要让包装后的对象仍可作为 Car 使用,但包装对象与原对象不是同一个实例。下面补齐基础车和委托关系:

interface Car {
    String info();
}

class RunCar implements Car {
    public String info() {
        return "run";
    }
}

abstract class CarDecorator implements Car {
    protected final Car car;
    CarDecorator(Car car) {
        this.car = java.util.Objects.requireNonNull(car);
    }
}

class FlyCar extends CarDecorator {
    FlyCar(Car car) { super(car); }
    public String info() { return car.info() + ", fly"; }
}

class SwimCar extends CarDecorator {
    SwimCar(Car car) { super(car); }
    public String info() { return car.info() + ", swim"; }
}

在调用方法内,new SwimCar(new FlyCar(new RunCar())).info() 表达先跑、再附加飞行与游泳说明。这是行为组合示意,不是车辆能力实现;真实装饰顺序可能影响结果、异常和资源关闭。

Java I/O 是可观察的委托例子:FilterInputStream 持有底层输入流,子类可在委托基础上增加功能。不要把它误写为“装饰类必须继承同一个具体父类”;保持需要的对外契约才是关键。

适配:把已有接口转成调用方需要的接口

假设旧调用方接受 Enumeration,新数据源给的是 Iterator,可明确转换方向:

class IteratorEnumeration<E> implements java.util.Enumeration<E> {
    private final java.util.Iterator<E> source;

    IteratorEnumeration(java.util.Iterator<E> source) {
        this.source = java.util.Objects.requireNonNull(source);
    }

    public boolean hasMoreElements() {
        return source.hasNext();
    }

    public E nextElement() {
        return source.next();
    }
}

调用方仍看到 hasMoreElements/nextElement,内部委托 hasNext/next。方法名是 nextElement,不是 nexElement。

Enumeration API 也提示 Iterator 有可选删除操作;转换接口不会凭空提供源对象没有的行为。适配不仅是重命名,还要看异常、可修改性和生命周期约定。

外观:让调用方少了解子系统内部结构

从调用方写:

return station.getThermometer().getTemperature();

改成:

return station.getTemperature();

可以把温度计选择和读取细节收进 station,由它提供更集中、稳定的入口。两行都是依赖应用对象的方法片段,不是独立程序。

简化入口不等于把子系统全部封死,也不是禁止任何方法链。应观察内部变化是否频繁向外传播,再决定外观边界。

代理:控制真实对象的访问方式

远程代理代表别处的服务,虚拟代理可延迟创建昂贵对象,保护代理负责访问限制。RMI 只是 Java 远程调用的一种技术,不等于代理模式本身。加载中占位图可帮助理解虚拟代理,但实际 UI 更新还要遵守相应线程约定。

Java 动态代理通过接口与 InvocationHandler 路由方法,核心写法可参照 Proxy API。以下片段中接口、真实对象和策略均由项目提供:

InvocationHandler handler = (proxy, method, args) -> {
    try {
        return method.invoke(target, args);
    } catch (InvocationTargetException failure) {
        throw failure.getCause();
    }
};
Object wrapped = Proxy.newProxyInstance(
        Api.class.getClassLoader(),
        new Class<?>[] { Api.class },
        handler);

涉及的类型来自 java.lang.reflect。示例只演示委托与异常展开,没有访问检查;打印日志也不会自动变成保护代理。运行时生成代理类不等于先生成 Java 源码再调用 javac,具体字节码实现应按 JDK 版本核对。

装饰偏向附加职责,适配改变调用方看到的接口,外观收拢子系统入口,代理控制访问边界。它们可以组合,甚至共享结构,但不应靠“都用了委托”就当成同一种设计。