| Files | Last commit | Last update |
|---|---|---|
| 1 month ago |
结构型模式
关注类和对象的组合方式,通过组合关系实现松耦合的灵活结构。
引言:结构型模式 — 把对象组合对
结构型模式关心类和对象如何组合成更大的结构——通过继承还是通过组合、是否暴露实现、是否在运行时改变结构。难点不在选 Adapter 还是 Bridge,而在区分"接口兼容"(Adapter)、"抽象与实现解耦"(Bridge)、"动态给对象加职责"(Decorator)vs "用代理控制访问"(Proxy)。本篇围绕"组合方式 + 暴露程度"两条轴,对比 Adapter/Bridge、Decorator/Proxy、Facade/Mediator 等高频二选一。
三、代理模式 Proxy
为其他对象提供一种代理,控制对这个对象的访问。
3.1 静态代理
/**
* 真实主题接口
*/
public interface UserService {
void save(String name);
}
public class UserServiceImpl implements UserService {
@Override public void save(String name) {
System.out.println("保存用户: " + name);
}
}
/**
* 静态代理类:编译期确定
*/
public class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void save(String name) {
System.out.println("[事务] 开启事务");
target.save(name);
System.out.println("[事务] 提交事务");
}
}
// 使用
UserService proxy = new UserServiceProxy(new UserServiceImpl());
proxy.save("张三");
3.2 JDK 动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK 动态代理:基于接口,运行时生成代理类
*/
public class JdkProxyHandler implements InvocationHandler {
private final Object target;
public JdkProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[前置增强] " + method.getName());
Object result = method.invoke(target, args);
System.out.println("[后置增强] " + method.getName());
return result;
}
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
// 使用
UserService target = new UserServiceImpl();
UserService proxy = new JdkProxyHandler(target).getProxy();
proxy.save("李四");
3.3 CGLIB 动态代理
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* CGLIB 代理:基于继承(无需接口),运行时生成子类
*/
public class CglibProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(Class<T> targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("[CGLIB 前置] " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("[CGLIB 后置] " + method.getName());
return result;
});
return (T) enhancer.create();
}
}
// 使用
UserServiceImpl proxy = CglibProxyFactory.createProxy(UserServiceImpl.class);
proxy.save("王五");
3.4 代理模式对比
| 代理方式 | 是否需要接口 | 性能 | 典型应用 |
|---|---|---|---|
| 静态代理 | 需要 | 最高 | 少量且确定的代理逻辑 |
| JDK 动态代理 | 必须需要接口 | 较高 | Spring AOP(有接口时) |
| CGLIB | 不需要 | 一般 | Spring AOP(无接口时) |
七、装饰器模式 Decorator
在不改变原对象的情况下,动态地给对象添加职责。
7.1 Java I/O 中的装饰器
import java.io.*;
// 标准 Java I/O 就是典型的装饰器模式:
// FileInputStream -> 节点流(Component 实现)
// BufferedInputStream -> 缓冲装饰器
// DataInputStream -> 数据类型装饰器
// GZIPInputStream -> 压缩装饰器
InputStream is = new GZIPInputStream( // 压缩解压
new DataInputStream( // 读取基本类型
new BufferedInputStream( // 缓冲
new FileInputStream("data.bin") // 文件输入
)));
7.2 自定义装饰器
/**
* 组件接口
*/
public interface Coffee {
String getDescription();
double cost();
}
/**
* 具体组件
*/
public class SimpleCoffee implements Coffee {
@Override public String getDescription() { return "纯咖啡"; }
@Override public double cost() { return 10.0; }
}
/**
* 装饰器基类
*/
public abstract class CoffeeDecorator implements Coffee {
protected final Coffee delegate;
protected CoffeeDecorator(Coffee delegate) {
this.delegate = delegate;
}
@Override public String getDescription() { return delegate.getDescription(); }
@Override public double cost() { return delegate.cost(); }
}
/**
* 加牛奶装饰
*/
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee delegate) { super(delegate); }
@Override public String getDescription() {
return delegate.getDescription() + " + 牛奶";
}
@Override public double cost() {
return delegate.cost() + 3.0;
}
}
/**
* 加糖装饰
*/
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee delegate) { super(delegate); }
@Override public String getDescription() {
return delegate.getDescription() + " + 糖";
}
@Override public double cost() {
return delegate.cost() + 1.0;
}
}
// 使用
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription()); // 纯咖啡 + 牛奶 + 糖
System.out.println(coffee.cost()); // 14.0
7.3 装饰器 vs 代理 vs 适配器
| 维度 | 装饰器 | 代理 | 适配器 |
|---|---|---|---|
| 意图 | 增加功能 | 控制访问 | 接口转换 |
| 接口 | 与原对象相同 | 与原对象相同 | 与原对象不同 |
| 关系 | 可以层层嵌套 | 通常一对一 | 一对一 |
十、适配器模式 Adapter
将一个类的接口转换成客户期望的另一个接口。
10.1 类适配器(继承)
/**
* 目标接口(客户端期望的接口)
*/
public interface Target {
void request();
}
/**
* 被适配者(已存在的、不兼容的类)
*/
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee 的特殊方法");
}
}
/**
* 类适配器:通过继承适配
*/
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest(); // 调用父类方法
}
}
// 使用
Target target = new ClassAdapter();
target.request(); // Adpatee 的特殊方法
10.2 对象适配器(组合)
/**
* 对象适配器:通过组合适配(更推荐,符合组合优于继承)
*/
public class ObjectAdapter implements Target {
private final Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
10.3 接口适配器(默认实现)
/**
* 大接口:有 5 个方法
*/
public interface WindowListener {
void onOpen();
void onClose();
void onFocus();
void onBlur();
void onResize();
}
/**
* 默认适配器:让子类只需覆盖关心的方法
*/
public abstract class WindowAdapter implements WindowListener {
@Override public void onOpen() {}
@Override public void onClose() {}
@Override public void onFocus() {}
@Override public void onBlur() {}
@Override public void onResize() {}
}
// 使用
WindowListener listener = new WindowAdapter() {
@Override public void onClose() {
System.out.println("窗口关闭");
}
};
10.4 实际应用举例
| 场景 | 被适配 | 适配器 | 目标接口 |
|---|---|---|---|
InputStreamReader |
InputStream(字节流) |
字符编码转换 | Reader(字符流) |
Spring HandlerAdapter |
各种 Handler |
统一调度 | HandlerAdapter.handle() |
JDK Arrays.asList() |
数组 | 包装为 List | List 接口 |