Java设计模式对比与选型指南:策略/状态/代理/装饰器
设计模式对比与选型指南:如何选择最合适的解决方案
作为Java开发者,我们经常面临设计模式的选择问题。相似的模式之间往往存在微妙差异,而不同的应用场景也需要不同的模式组合。本文将深入分析常见模式的对比关系,并提供场景化的选型建议。
一、相似模式对比
1. 策略模式 vs 状态模式
策略模式和状态模式在结构上非常相似,都通过组合方式将行为委托给其他对象,但它们的意图不同:
关键区别:
- 策略模式:客户端主动选择算法策略,策略之间相互独立
- 状态模式:状态转换由上下文或状态对象控制,状态之间可能有关联
实践建议:
- 当需要根据不同条件选择不同算法时用策略模式(如支付方式选择)
- 当对象行为随内部状态改变时用状态模式(如订单状态流转)
2. 代理模式 vs 装饰器模式
代理模式和装饰器模式都通过包装对象来增强功能,但目的不同:
// 代理模式示例
interface Image {
void display();
}
class RealImage implements Image {
public void display() {
System.out.println("显示真实图片");
}
}
class ProxyImage implements Image {
private RealImage realImage;
public void display() {
if (realImage == null) {
realImage = new RealImage(); // 延迟加载
}
realImage.display();
}
}
// 装饰器模式示例
interface Shape {
void draw();
}
class Circle implements Shape {
public void draw() {
System.out.println("画圆");
}
}
abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
public void draw() {
decoratedShape.draw();
}
}
class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
public void draw() {
decoratedShape.draw();
setRedBorder();
}
private void setRedBorder() {
System.out.println("设置红色边框");
}
}
关键区别:
- 代理模式:控制访问,通常对客户端透明(如远程代理、虚拟代理)
- 装饰器模式:动态添加职责,客户端可以自由组合装饰
实践建议:
- 需要控制对象访问或添加间接层时用代理模式(如权限控制、延迟加载)
- 需要动态透明地扩展功能时用装饰器模式(如IO流处理)
3. 工厂方法 vs 抽象工厂
工厂方法和抽象工厂都是创建型模式,但抽象层次不同:
关键区别:
- 工厂方法:针对单个产品等级结构,通过子类决定实例化哪个类
- 抽象工厂:针对多个产品族,提供一个创建一系列相关对象的接口
实践建议:
- 当系统只需要创建一个产品时用工厂方法(如日志记录器)
- 当需要创建多个相关产品时用抽象工厂(如跨平台UI组件)
二、场景化选型指南
1. 对象创建场景选型
场景特征 | 推荐模式 | 典型案例 |
---|---|---|
全局唯一对象 | 单例模式 | 配置管理器、线程池 |
复杂对象的逐步构建 | 建造者模式 | SQL查询构建、AlertDialog配置 |
需要隔离具体类 | 工厂方法/抽象工厂 | 跨数据库访问、插件系统 |
需要高性能对象复制 | 原型模式 | 游戏实体克隆、配置模板 |
代码示例:建造者模式实现复杂对象创建
public class Computer {
private final String CPU;
private final String RAM;
private final int storage;
private Computer(Builder builder) {
this.CPU = builder.CPU;
this.RAM = builder.RAM;
this.storage = builder.storage;
}
public static class Builder {
private String CPU = "i5";
private String RAM = "8GB";
private int storage = 256;
public Builder setCPU(String CPU) {
this.CPU = CPU;
return this;
}
public Builder setRAM(String RAM) {
this.RAM = RAM;
return this;
}
public Builder setStorage(int storage) {
this.storage = storage;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
// 使用方式
Computer myPC = new Computer.Builder()
.setCPU("i7")
.setRAM("16GB")
.setStorage(512)
.build();
2. 行为扩展场景选型
场景特征 | 推荐模式 | 典型案例 |
---|---|---|
透明地动态添加功能 | 装饰器模式 | Java I/O流、Web拦截器 |
处理链式请求 | 责任链模式 | 审批流程、过滤器链 |
算法族互换 | 策略模式 | 支付方式、排序算法 |
状态驱动行为变化 | 状态模式 | 订单状态机、游戏角色状态 |
实践建议:
- 装饰器模式适合运行时添加功能且需要保持接口一致的场景
- 责任链模式适合需要动态决定处理者的场景(如权限校验链)
- 当行为变化频繁且需要隔离变化时优先考虑策略模式
3. 性能敏感场景选型
性能需求 | 推荐模式 | 优化要点 |
---|---|---|
大量细粒度对象共享 | 享元模式 | 对象池化、内部状态外部化 |
高成本对象复制 | 原型模式 | 浅拷贝/深拷贝优化 |
复杂对象初始化开销大 | 建造者模式 | 分步构建、延迟初始化 |
频繁算法切换 | 策略模式+缓存 | 预编译策略、避免重复创建 |
代码示例:享元模式优化内存使用
public class FlyweightPattern {
interface Shape {
void draw();
}
static class Circle implements Shape {
private String color;
public Circle(String color) {
this.color = color;
}
public void draw() {
System.out.println("画一个" + color + "的圆");
}
}
static class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle)circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("创建" + color + "的圆");
}
return circle;
}
}
public static void main(String[] args) {
String[] colors = {"红", "绿", "蓝", "红", "绿"};
for (String color : colors) {
Shape circle = ShapeFactory.getCircle(color);
circle.draw();
}
}
}
三、综合选型决策树
四、模式组合的最佳实践
在实际项目中,设计模式往往需要组合使用:
Spring框架中的典型组合:
- 工厂方法(BeanFactory) + 单例模式(默认作用域)
- 代理模式(AOP) + 模板方法(JdbcTemplate)
高性能缓存实现:
public class CachedDataService { // 单例保证全局唯一缓存 private static volatile CachedDataService instance; private final Map<String, Object> cache; // 建造者配置缓存参数 public static class Builder { private int maxSize = 1000; public Builder setMaxSize(int maxSize) { this.maxSize = maxSize; return this; } public CachedDataService build() { return new CachedDataService(this); } } private CachedDataService(Builder builder) { // 享元模式实现缓存池 this.cache = Collections.synchronizedMap( new LinkedHashMap<String, Object>(16, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > builder.maxSize; } }); } // 策略模式支持多种缓存策略 public interface CacheStrategy { Object loadData(String key); } public Object getData(String key, CacheStrategy strategy) { return cache.computeIfAbsent(key, strategy::loadData); } }
电商系统中的模式应用:
- 订单状态管理:状态模式
- 支付方式选择:策略模式
- 促销活动叠加:装饰器模式
- 商品分类展示:组合模式
- 库存中心访问:代理模式(远程调用)
五、常见误区与规避建议
过度设计反模式:
- 问题:在不必要时引入复杂模式
- 建议:遵循YAGNI原则,从简单实现开始重构
单例滥用:
- 问题:导致测试困难、隐藏依赖
- 建议:考虑依赖注入替代硬编码单例
装饰器陷阱:
- 问题:装饰顺序影响结果
- 建议:明确文档说明或限制装饰顺序
工厂方法膨胀:
- 问题:每个产品类对应一个工厂类
- 建议:使用参数化工厂方法减少类数量
总结
设计模式的选择应当基于以下因素综合考虑:
- 需要解决的问题本质(创建、结构、行为)
- 系统的可扩展性需求
- 性能约束条件
- 团队熟悉程度
- 与现有架构的契合度
记住:没有最好的模式,只有最合适的模式。在实际开发中,往往需要根据具体场景对标准模式进行适当调整或组合使用。