🧩 结构型模式
代理 · 装饰器 · 适配器 · 外观 · 享元 · 桥接,含易混对比
1. 代理模式?静态代理和动态代理?
代理模式:为对象提供一个代理对象,客户端访问代理,代理控制对真实对象的访问(增强、延迟、隔离)。三种:
- 静态代理:代理类编译期写死,一个接口一个代理类。缺点:类爆炸
- JDK 动态代理:运行时 Proxy 生成实现类,InvocationHandler 统一处理(详见 Java 基础页)
- CGLIB:继承生成子类代理,无需接口
应用:Spring AOP、MyBatis Mapper 代理、RPC 远程调用(本地接口调远程服务)、日志/监控/权限
🎯 面试要点
- 代理 vs 装饰器:代理控制"是否调用/何时调用目标"(目标不透明);装饰器增强"调用过程本身"(目标透明,可层层包装)——最常考的对比
- 代理 vs 适配器:代理保持接口不变;适配器转换接口
2. 装饰器模式?Java 中的经典应用?
装饰器:动态给对象叠加功能,替代继承扩展。结构:装饰器实现同一接口,持有被装饰者引用,调用前后附加逻辑。
Java 经典案例:IO 流全家桶——BufferedInputStream 装饰 FileInputStream(加缓冲),DataInputStream 再加解析能力。层层包装。JDK 源码 BufferedInputStream 构造器接收 InputStream。
装饰器示意
interface Coffee { double cost(); }
class PlainCoffee implements Coffee {
public double cost() { return 10; }
}
class MilkDecorator implements Coffee { // 装饰器
private final Coffee base;
MilkDecorator(Coffee base) { this.base = base; }
public double cost() { return base.cost() + 3; } // 叠加功能
}
// new MilkDecorator(new PlainCoffee()).cost() == 13
🎯 面试要点
- 继承的替代品:运行时组合优于继承(装饰器模式正是"组合优于继承"的实践)
- 对比继承:功能组合爆炸问题(咖啡×加料 = 2ⁿ 个类)
3. 适配器模式?应用场景?
适配器:把不兼容的接口转换成客户端期望的接口。两种实现:类适配器(继承)、对象适配器(组合,推荐)。
Java 应用:
- InputStreamReader/OutputStreamWriter:字节流 ↔ 字符流适配
- Arrays.asList():数组 → List
- Spring MVC 的 HandlerAdapter:不同 Controller 类型(@Controller/HttpRequestHandler/注解方法)统一成 DispatcherServlet 可调用
- 日志门面 slf4j 的 binding(logback/log4j 适配)
🎯 面试要点
- 适配器 vs 外观:适配器是"接口转换"(一个对象),外观是"子系统统一入口"(一组对象)
- 适配器 vs 桥接:适配器让"已有"的东西协作;桥接分离"抽象与实现"两个维度(如形状×颜色)
4. 外观模式和享元模式?
- 外观(Facade):为复杂子系统提供统一简化入口。案例:公司门禁系统(用户只管刷卡)、三层架构的 Service 层、
TransactionTemplate(JdbcTemplate 更准确的归类是模板方法,见 spring-patterns 页) - 享元(Flyweight):共享细粒度对象减少内存。核心:内部状态(可共享、不变)+ 外部状态(随场景传入)。案例:字符串常量池、Integer 缓存(-128~127)、Long/Boolean 的 valueOf 缓存
🎯 面试要点
- 享元 ≠ 池化:享元是把大量细粒度对象的可共享部分抽出来共用(如字符、小整数);连接池/线程池是资源复用 + 限流,同一时刻一个资源只服务一个使用者。两者思想同源但不等价,面试被追问时别说成同一个
- Integer.valueOf() 缓存面试高频:== 比较陷阱(-128~127 命中缓存)