结构型模式
medium设计模式结构型代理模式适配器模式装饰器模式外观模式
代理模式(Proxy)
为目标对象提供一个替身,控制对目标对象的访问。
比喻:明星和经纪人。粉丝(客户端)不直接联系明星(真实对象),而是通过经纪人(代理)。经纪人可以在转达前做过滤、记录等额外工作。
// 接口
public interface UserService {
void save(User user);
}
// 真实对象
public class UserServiceImpl implements UserService {
public void save(User user) {
// 保存用户到数据库
}
}
// 静态代理
public class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
public void save(User user) {
System.out.println("开始事务"); // 增强:前置处理
target.save(user); // 调用真实对象
System.out.println("提交事务"); // 增强:后置处理
}
}
动态代理(JDK / CGLIB)是 Spring AOP 的核心实现:运行时动态生成代理类,不需要手写代理。
适配器模式(Adapter)
将一个类的接口转换为客户端期望的另一个接口,使接口不兼容的类可以一起工作。
比喻:电源转换插头。中国插头(已有接口)在美国需要一个转换器(适配器)才能插入美国插座(目标接口)。
// 目标接口(客户端期望的)
public interface MediaPlayer {
void play(String filename);
}
// 已有的类(接口不兼容)
public class VlcPlayer {
public void playVlc(String filename) { /* ... */ }
}
// 适配器
public class VlcAdapter implements MediaPlayer {
private VlcPlayer vlcPlayer = new VlcPlayer();
public void play(String filename) {
vlcPlayer.playVlc(filename); // 将 play 转换为 playVlc
}
}
装饰器模式(Decorator)
动态地给对象添加额外功能,比继承更灵活。
比喻:给咖啡加配料。一杯基础咖啡可以加牛奶、加糖、加奶油——每种配料都是一个"装饰",可以自由组合。
// Java I/O 就是典型的装饰器模式
InputStream is = new FileInputStream("file.txt"); // 基础流
InputStream bis = new BufferedInputStream(is); // 装饰:加缓冲
InputStream dis = new DataInputStream(bis); // 装饰:加数据类型读取
// 自由组合,层层包装
代理 vs 装饰器
| 对比 | 代理模式 | 装饰器模式 |
|---|---|---|
| 目的 | 控制访问(增加权限检查、日志等) | 增强功能(添加新行为) |
| 语义 | 客户端不知道代理的存在 | 客户端知道在使用装饰 |
| 典型应用 | Spring AOP、RPC 调用 | Java I/O Streams |
外观模式(Facade)
为复杂子系统提供一个简化的统一接口。
// 没有外观模式:客户端需要了解每个子系统
cpu.freeze();
memory.load(0x00, data);
cpu.jump(0x00);
cpu.execute();
// 外观模式:提供简化接口
computer.start(); // 内部封装了上述复杂调用
生产高频题
代理模式和装饰器模式的区别?
代理模式的目的是控制访问(权限、日志、远程调用),客户端通常不知道代理的存在。装饰器模式的目的是增强功能(动态添加新行为),客户端主动选择装饰。结构类似但意图不同。
JDK 动态代理和 CGLIB 的区别?
JDK 动态代理基于接口(目标类必须实现接口),通过 Proxy.newProxyInstance() 生成代理。CGLIB 基于继承(通过字节码生成目标类的子类),无需接口但不能代理 final 类/方法。Spring AOP 在有接口时默认用 JDK 代理,无接口时用 CGLIB。