设计模式技能
实用的 Java 设计模式参考,附带现代示例。
使用时机
- 用户要求实现特定模式
- 设计可扩展/灵活的组件
- 重构僵化的代码结构
- 代码审查建议使用模式
快速参考:何时使用何种模式
| 问题 | 模式 |
|---|---|
| 复杂的对象构造 | 构建器 (Builder) |
| 无需指定类即可创建对象 | 工厂 (Factory) |
| 多种算法,运行时切换 | 策略 (Strategy) |
| 无需修改类即可添加行为 | 装饰器 (Decorator) |
| 通知多个对象状态变化 | 观察者 (Observer) |
| 确保只有一个实例 | 单例 (Singleton) |
| 转换不兼容的接口 | 适配器 (Adapter) |
| 定义算法骨架 | 模板方法 (Template Method) |
创建型模式
构建器 (Builder)
使用时机: 对象参数过多,部分可选。
// ❌ 伸缩构造函数反模式
public class User {
public User(String name) { }
public User(String name, String email) { }
public User(String name, String email, int age) { }
public User(String name, String email, int age, String phone) { }
// ... 构造函数爆炸
}
// ✅ 构建器模式
public class User {
private final String name; // 必需
private final String email; // 必需
private final int age; // 可选
private final String phone; // 可选
private final String address; // 可选
private User(Builder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
public static Builder builder(String name, String email) {
return new Builder(name, email);
}
public static class Builder {
// 必需
private final String name;
private final String email;
// 可选,带默认值
private int age = 0;
private String phone = "";
private String address = "";
private Builder(String name, String email) {
this.name = name;
this.email = email;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder phone(String phone) {
this.phone = phone;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public User build() {
return new User(this);
}
}
}
// 使用
User user = User.builder("John", "[email protected]")
.age(30)
.phone("+1234567890")
.build();
使用 Lombok:
@Builder
@Getter
public class User {
private final String name;
private final String email;
@Builder.Default private int age = 0;
private String phone;
}
工厂方法 (Factory Method)
使用时机: 需要创建对象但不指定具体类。
// ✅ 工厂方法模式
public interface Notification {
void send(String message);
}
public class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Email: " + message);
}
}
public class SmsNotification implements Notification {
@Override
public void send(String message) {
System.out.println("SMS: " + message);
}
}
public class PushNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Push: " + message);
}
}
// 工厂
public class NotificationFactory {
public static Notification create(String type) {
return switch (type.toUpperCase()) {
case "EMAIL" -> new EmailNotification();
case "SMS" -> new SmsNotification();
case "PUSH" -> new PushNotification();
default -> throw new IllegalArgumentException("未知类型: " + type);
};
}
}
// 使用
Notification notification = NotificationFactory.create("EMAIL");
notification.send("Hello!");
使用 Spring(推荐):
public interface NotificationSender {
void send(String message);
String getType();
}
@Component
public class EmailSender implements NotificationSender {
@Override public void send(String message) { /* ... */ }
@Override public String getType() { return "EMAIL"; }
}
@Component
public class SmsSender implements NotificationSender {
@Override public void send(String message) { /* ... */ }
@Override public String getType() { return "SMS"; }
}
@Component
public class NotificationFactory {
private final Map<String, NotificationSender> senders;
public NotificationFactory(List<NotificationSender> senderList) {
this.senders = senderList.stream()
.collect(Collectors.toMap(
NotificationSender::getType,
Function.identity()
));
}
public NotificationSender getSender(String type) {
return Optional.ofNullable(senders.get(type))
.orElseThrow(() -> new IllegalArgumentException("未知: " + type));
}
}
单例 (Singleton)
使用时机: 恰好需要一个实例(请谨慎使用!)。
// ✅ 现代单例(基于枚举,线程安全)
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
DatabaseConnection() {
// 初始化连接
}
public Connection getConnection() {
return connection;
}
}
// 使用
Connection conn = DatabaseConnection.INSTANCE.getConnection();
使用 Spring(推荐):
@Component // 默认作用域是单例
public class DatabaseConnection {
// Spring 管理单个实例
}
警告: 单例可能带来问题:
- 难以测试(全局状态)
- 隐藏依赖
- 考虑使用依赖注入替代
行为型模式
策略 (Strategy)
使用时机: 同一操作有多种算法,需要在运行时切换。
// ✅ 策略模式
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
public class CreditCardPayment implements PaymentStrategy {
private final String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(BigDecimal amount) {
System.out.println("Paid " + amount + " with card " + cardNumber);
}
}
public class PayPalPayment implements PaymentStrategy {
private final String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(BigDecimal amount) {
System.out.println("Paid " + amount + " via PayPal: " + email);
}
}
public class CryptoPayment implements PaymentStrategy {
private final String walletAddress;
public CryptoPayment(String walletAddress) {
this.walletAddress = walletAddress;
}
@Override
public void pay(BigDecimal amount) {
System.out.println("Paid " + amount + " to wallet: " + walletAddress);
}
}
// 上下文
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(BigDecimal total) {
paymentStrategy.pay(total);
}
}
// 使用
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment("4111-1111-1111-1111"));
cart.checkout(new BigDecimal("99.99"));
// 运行时切换策略
cart.setPaymentStrategy(new PayPalPayment("[email protected]"));
cart.checkout(new BigDecimal("49.99"));
使用 Java 8+(函数式):
// 策略作为函数式接口
@FunctionalInterface
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
// 使用 Lambda
PaymentStrategy creditCard = amount ->
System.out.println("Card payment: " + amount);
PaymentStrategy paypal = amount ->
System.out.println("PayPal payment: " + amount);
cart.setPaymentStrategy(creditCard);
观察者 (Observer)
使用时机: 对象需要被通知另一个对象的更改。
// ✅ 观察者模式(现代 Java)
public interface OrderObserver {
void onOrderPlaced(Order order);
}
public class OrderService {
private final List<OrderObserver> observers = new ArrayList<>();
public void addObserver(OrderObserver observer) {
observers.add(observer);
}
public void removeObserver(OrderObserver observer) {
observers.remove(observer);
}
public void placeOrder(Order order) {
// 处理订单
saveOrder(order);
// 通知所有观察者
observers.forEach(observer -> observer.onOrderPlaced(order));
}
}
// 观察者
public class InventoryService implements OrderObserver {
@Override
public void onOrderPlaced(Order order) {
// 减少库存
order.getItems().forEach(item ->
reduceStock(item.getProductId(), item.getQuantity())
);
}
}
public class EmailNotificationService implements OrderObserver {
@Override
public void onOrderPlaced(Order order) {
sendConfirmationEmail(order.getCustomerEmail(), order);
}
}
public class AnalyticsService implements OrderObserver {
@Override
public void onOrderPlaced(Order order) {
trackOrderEvent(order);
}
}
// 设置
OrderService orderService = new OrderService();
orderService.addObserver(new InventoryService());
orderService.addObserver(new EmailNotificationService());
orderService.addObserver(new AnalyticsService());
使用 Spring 事件(推荐):
// 事件
public record OrderPlacedEvent(Order order) {}
// 发布者
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public void placeOrder(Order order) {
saveOrder(order);
eventPublisher.publishEvent(new OrderPlacedEvent(order));
}
}
// 监听器(观察者)
@Component
public class InventoryListener {
@EventListener
public void handleOrderPlaced(OrderPlacedEvent event) {
// 减少库存
}
}
@Component
public class EmailListener {
@EventListener
public void handleOrderPlaced(OrderPlacedEvent event) {
// 发送邮件
}
@EventListener
@Async // 异步处理
public void handleOrderPlacedAsync(OrderPlacedEvent event) {
// 异步发送邮件
}
}
模板方法 (Template Method)
使用时机: 定义算法骨架,让子类填充步骤。
// ✅ 模板方法模式
public abstract class DataProcessor {
// 模板方法 - 定义算法
public final void process() {
readData();
processData();
writeData();
if (shouldNotify()) {
notifyCompletion();
}
}
// 由子类实现的步骤
protected abstract void readData();
protected abstract void processData();
protected abstract void writeData();
// 钩子 - 可选的覆盖
protected boolean shouldNotify() {
return true;
}
protected void notifyCompletion() {
System.out.println("Processing completed!");
}
}
public class CsvDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("Reading CSV file...");
}
@Override
protected void processData() {
System.out.println("Processing CSV data...");
}
@Override
protected void writeData() {
System.out.println("Writing to database...");
}
}
public class ApiDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("Fetching from API...");
}
@Override
protected void processData() {
System.out.println("Transforming API response...");
}
@Override
protected void writeData() {
System.out.println("Writing to cache...");
}
@Override
protected boolean shouldNotify() {
return false; // 覆盖钩子
}
}
// 使用
DataProcessor csvProcessor = new CsvDataProcessor();
csvProcessor.process();
DataProcessor apiProcessor = new ApiDataProcessor();
apiProcessor.process();
结构型模式
装饰器 (Decorator)
使用时机: 不修改现有类,动态添加行为。
// ✅ 装饰器模式
public interface Coffee {
String getDescription();
BigDecimal getCost();
}
public class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Coffee";
}
@Override
public BigDecimal getCost() {
return new BigDecimal("2.00");
}
}
// 基础装饰器
public abstract class CoffeeDecorator implements Coffee {
protected final Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public BigDecimal getCost() {
return coffee.getCost();
}
}
// 具体装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Milk";
}
@Override
public BigDecimal getCost() {
return coffee.getCost().add(new BigDecimal("0.50"));
}
}
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Sugar";
}
@Override
public BigDecimal getCost() {
return coffee.getCost().add(new BigDecimal("0.20"));
}
}
public class WhippedCreamDecorator extends CoffeeDecorator {
public WhippedCreamDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Whipped Cream";
}
@Override
public BigDecimal getCost() {
return coffee.getCost().add(new BigDecimal("0.70"));
}
}
// 使用
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getDescription() + " $" + coffee.getCost());
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
coffee = new WhippedCreamDecorator(coffee);
System.out.println(coffee.getDescription() + " $" + coffee.getCost());
// 输出:Coffee, Milk, Sugar, Whipped Cream $3.40
适配器 (Adapter)
使用时机: 让不兼容的接口协同工作。
// ✅ 适配器模式
// 现有接口
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// 高级播放器(不兼容)
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file: " + fileName);
}
@Override
public void playMp4(String fileName) {
// 不执行操作
}
}
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// 不执行操作
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file: " + fileName);
}
}
// 适配器
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
// 客户端
public class AudioPlayer implements MediaPlayer {
private MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
// 内置支持播放 mp3
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file: " + fileName);
}
// 适配器支持其他格式
else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
// 使用
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond_the_horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far_far_away.vlc");
audioPlayer.play("avi", "mind_me.avi");
反模式与陷阱
何时应该避免使用模式
| 陷阱 | 修复 |
|---|---|
| 过度设计 | 编码前先简单测试 |
| 模式崇拜 | 解决问题,不要强套模式 |
| 过早抽象 | 重复三次后再考虑抽象 |
| 接口臃肿 | 接口要保持专注(接口隔离原则,ISP) |
选择模式的检查清单
- 这是什么类型的问题?(创建型、结构型、行为型)
- 变化在哪里?(封装变化点)
- 复杂度是否值得?(能否用更简单的方案解决?)
- 模式会提高还是降低可读性?(团队是否熟悉该模式?)
- 能否利用框架已有的支持?(Spring、Java 8+ 特性)
最佳实践
现代 Java 与框架
- 优先使用库函数: 在自定义实现之前,先考虑 Stream API、Optional、
java.util.function包。 - 利用框架: Spring 的 IoC 容器已经实现了工厂、单例和观察者模式。
- 注解胜过继承: 尽可能使用 Lombok 的 @Builder 和 Spring 的 @EventListener。
- 考虑函数式等价方案: Lambda 和方法引用通常可以替代策略和命令模式。
- 模式组合: 模式可以组合使用(例如,工厂与策略、构建器与装饰器)。
测试考虑
- 策略: 每个策略应该可以独立测试。
- 观察者: 测试事件发布,而非监听器实现。
- 构建器: 测试部分对象创建(可选参数留空)。
- 工厂: 工厂的测试应只返回模拟对象(Mock)。
- 单例: 在测试中使用依赖注入覆盖单例实例。