我刚入行Java开发时,对设计模式的理解就是"23种花哨的编程技巧"——直到接手第一个大型遗留系统。那个充斥着重复代码和紧耦合的代码库,让我深刻理解了设计模式的价值。设计模式不是炫技,而是前辈们用血泪教训总结出来的最佳实践。
设计模式本质上是对常见软件设计问题的标准化解决方案。就像建筑领域的标准结构设计一样,它们提供了可复用的设计模板。在Java生态中,从Spring框架到J2EE规范,设计模式无处不在。掌握它们能让你:
新手常犯的错误是强行套用模式。记住:模式是工具,不是目标。只有当你的代码出现了"坏味道"(如重复代码、难以扩展),才需要考虑是否适用某个模式。
创建型模式处理对象实例化过程,让系统不依赖于具体的对象创建方式。以工厂方法模式为例:
java复制public interface Logger {
void log(String message);
}
public class FileLogger implements Logger {
@Override
public void log(String message) {
// 写入文件
}
}
public abstract class LoggerFactory {
public abstract Logger createLogger();
public void log(String message) {
Logger logger = createLogger();
logger.log(message);
}
}
这种方式的优势在于:
我在电商项目中就吃过亏:初期直接new各种支付处理器,后期新增支付方式时不得不修改数十处代码。改用抽象工厂模式后,新增支付方式只需实现新类即可。
结构型模式关注类和对象的组合方式。装饰器模式是我最常用的模式之一,它在不修改原有类的情况下扩展功能。Java I/O流就是经典案例:
java复制InputStream input = new BufferedInputStream(
new FileInputStream("data.txt"));
这种嵌套结构的好处是:
实际开发中,过度使用装饰器会导致调用链过长。我的经验是:当装饰层数超过3层时,就该考虑重构了。
行为型模式解决对象间的交互问题。观察者模式在事件驱动系统中尤为常见:
java复制public class Order {
private List<OrderObserver> observers = new ArrayList<>();
public void addObserver(OrderObserver o) {
observers.add(o);
}
public void complete() {
for (OrderObserver o : observers) {
o.onCompleted(this);
}
}
}
这样设计的优势:
在微服务架构中,我常用观察者模式实现本地事件总线,替代部分MQ场景,减少系统复杂度。
单例看似简单,但坑很多。以下是线程安全的实现:
java复制public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
关键点:
单例在Spring中通常由容器管理。我的经验是:除非是重量级资源(如数据库连接池),否则优先考虑依赖注入而非单例。
当遇到这样的代码时:
java复制public class PaymentService {
public void pay(String type) {
if ("alipay".equals(type)) {
// 支付宝逻辑
} else if ("wechat".equals(type)) {
// 微信支付逻辑
}
// 更多if-else...
}
}
就该考虑策略模式了:
java复制public interface PaymentStrategy {
void pay(BigDecimal amount);
}
public class PaymentService {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void pay(BigDecimal amount) {
strategy.pay(amount);
}
}
改进后:
Spring的JdbcTemplate就是典型应用:
java复制public abstract class JdbcTemplate {
public final Object execute(String sql) {
Connection con = null;
try {
con = getConnection();
PreparedStatement ps = con.prepareStatement(sql);
// 执行查询
return doInStatement(ps); // 抽象方法
} finally {
closeConnection(con);
}
}
protected abstract Object doInStatement(PreparedStatement ps);
}
这种模式的价值在于:
我在开发报表引擎时,就用模板方法统一处理了数据获取、格式转换等通用流程。
在实际项目中,模式往往组合使用。比如用抽象工厂创建一系列相关对象,这些对象又使用策略模式进行行为配置。Spring框架就是模式组合的大师级案例:
我曾用组合模式+访问者模式实现了一个复杂的权限系统:
java复制public interface PermissionVisitor {
void visit(MenuPermission permission);
void visit(OperationPermission permission);
}
public class RolePermissionCheck implements PermissionVisitor {
private boolean hasPermission;
public void visit(MenuPermission permission) {
// 检查菜单权限
}
public void visit(OperationPermission permission) {
// 检查操作权限
}
}
设计模式滥用比不用更危险。我见过最夸张的是一个简单CRUD接口用了7种模式,导致维护成本飙升。这些是危险信号:
判断标准:如果向团队解释设计需要超过5分钟,很可能过度设计了。
Java 8的函数式特性让某些模式实现更简洁。比如策略模式:
java复制public class PaymentService {
private Consumer<BigDecimal> strategy;
public void pay(BigDecimal amount) {
strategy.accept(amount);
}
}
// 使用
service.setStrategy(amount -> alipayClient.pay(amount));
命令模式也可以用Lambda简化:
java复制public class CommandTest {
public static void main(String[] args) {
List<Runnable> commands = Arrays.asList(
() -> System.out.println("Command 1"),
() -> System.out.println("Command 2")
);
commands.forEach(Runnable::run);
}
}
在微服务场景下,模式应用有了新变化:
我在设计配置中心时,就用了代理模式+缓存实现配置热更新:
java复制public class ConfigProxy implements Config {
private Config realConfig;
private LocalCache cache;
public String get(String key) {
if (cache.isExpired(key)) {
refresh(key);
}
return cache.get(key);
}
}
我保持的习惯是:每学习一个新模式,就找项目中能应用的地方进行重构。比如发现多个子类有相似方法时,就考虑模板方法模式;遇到复杂对象创建时,考虑建造者模式。
最后记住:设计模式是手段,不是目的。代码的简洁性和可维护性才是终极目标。经过多年实践,我现在写代码时会自然想到模式应用,而不是刻意套用——这大概就是所谓的"无招胜有招"境界吧。