设计模式在Java中的实际应用:单例、工厂与观察者模式详解
设计模式是软件开发中经过时间考验的解决方案,它们为常见的设计问题提供了可重用的模板。在Java开发中,合理运用设计模式不仅能提高代码的可维护性和可扩展性,还能让团队协作更加高效。本文将深入探讨三种最常用的设计模式:单例模式、工厂模式和观察者模式,并通过实际代码示例展示它们在Java项目中的应用。
一、单例模式(Singleton Pattern)
1.1 模式概述
单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要严格控制资源访问或者需要协调系统行为的场景中非常有用。
1.2 实际应用场景
- 数据库连接池管理:避免创建多个连接池实例
- 日志管理器:统一管理应用程序的日志输出
- 配置管理器:全局配置信息的统一访问
- 缓存管理:内存缓存的统一管理
1.3 Java实现方式
懒汉式(线程安全)
public class DatabaseConnectionPool {private static volatile DatabaseConnectionPool instance;private final List<Connection> connections;private static final int POOL_SIZE = 10;// 私有构造函数private DatabaseConnectionPool() {connections = new ArrayList<>();initializePool();}// 双重检查锁定public static DatabaseConnectionPool getInstance() {if (instance == null) {synchronized (DatabaseConnectionPool.class) {if (instance == null) {instance = new DatabaseConnectionPool();}}}return instance;}private void initializePool() {for (int i = 0; i < POOL_SIZE; i++) {// 初始化数据库连接connections.add(createConnection());}}public Connection getConnection() {synchronized (connections) {if (!connections.isEmpty()) {return connections.remove(connections.size() - 1);}return createConnection();}}public void returnConnection(Connection connection) {synchronized (connections) {if (connections.size() < POOL_SIZE) {connections.add(connection);}}}private Connection createConnection() {// 实际的数据库连接创建逻辑return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password");}
}
枚举式(推荐)
public enum ConfigurationManager {INSTANCE;private final Properties config;ConfigurationManager() {config = new Properties();loadConfiguration();}private void loadConfiguration() {try (InputStream input = getClass().getResourceAsStream("/application.properties")) {config.load(input);} catch (IOException e) {throw new RuntimeException("Failed to load configuration", e);}}public String getProperty(String key) {return config.getProperty(key);}public String getProperty(String key, String defaultValue) {return config.getProperty(key, defaultValue);}public void setProperty(String key, String value) {config.setProperty(key, value);}
}// 使用示例
public class Application {public static void main(String[] args) {String dbUrl = ConfigurationManager.INSTANCE.getProperty("database.url");System.out.println("Database URL: " + dbUrl);}
}
1.4 优缺点分析
优点:
- 控制实例数量,节约系统资源
- 提供全局访问点,便于统一管理
- 延迟初始化,提高启动性能
缺点:
- 违反了单一职责原则
- 可能造成代码耦合度过高
- 单元测试相对困难
二、工厂模式(Factory Pattern)
2.1 模式概述
工厂模式提供了一种创建对象的方式,无需指定具体要创建的类。它将对象的创建逻辑封装在工厂类中,客户端只需要知道传入的参数,而不需要关心创建对象的具体过程。
2.2 实际应用场景
- 数据库驱动选择:根据配置创建不同的数据库连接
- 消息队列实现:根据需求创建不同的消息处理器
- 图形界面组件:根据操作系统创建相应的UI组件
- 支付方式处理:根据支付类型创建相应的支付处理器
2.3 Java实现方式
简单工厂模式
// 支付接口
public interface PaymentProcessor {boolean processPayment(BigDecimal amount, String accountInfo);String getPaymentMethod();
}// 具体支付实现
public class CreditCardProcessor implements PaymentProcessor {@Overridepublic boolean processPayment(BigDecimal amount, String cardNumber) {System.out.println("Processing credit card payment: $" + amount);// 信用卡支付逻辑return validateCreditCard(cardNumber) && chargeCard(amount, cardNumber);}@Overridepublic String getPaymentMethod() {return "Credit Card";}private boolean validateCreditCard(String cardNumber) {// 信用卡验证逻辑return cardNumber != null && cardNumber.length() == 16;}private boolean chargeCard(BigDecimal amount, String cardNumber) {// 实际扣款逻辑return true;}
}public class PayPalProcessor implements PaymentProcessor {@Overridepublic boolean processPayment(BigDecimal amount, String email) {System.out.println("Processing PayPal payment: $" + amount);// PayPal支付逻辑return validatePayPalAccount(email) && processPayPalPayment(amount, email);}@Overridepublic String getPaymentMethod() {return "PayPal";}private boolean validatePayPalAccount(String email) {return email != null && email.contains("@");}private boolean processPayPalPayment(BigDecimal amount, String email) {return true;}
}public class BankTransferProcessor implements PaymentProcessor {@Overridepublic boolean processPayment(BigDecimal amount, String accountNumber) {System.out.println("Processing bank transfer: $" + amount);// 银行转账逻辑return validateBankAccount(accountNumber) && processBankTransfer(amount, accountNumber);}@Overridepublic String getPaymentMethod() {return "Bank Transfer";}private boolean validateBankAccount(String accountNumber) {return accountNumber != null && accountNumber.length() >= 10;}private boolean processBankTransfer(BigDecimal amount, String accountNumber) {return true;}
}// 支付处理器工厂
public class PaymentProcessorFactory {public enum PaymentType {CREDIT_CARD, PAYPAL, BANK_TRANSFER}public static PaymentProcessor createProcessor(PaymentType type) {switch (type) {case CREDIT_CARD:return new CreditCardProcessor();case PAYPAL:return new PayPalProcessor();case BANK_TRANSFER:return new BankTransferProcessor();default:throw new IllegalArgumentException("Unsupported payment type: " + type);}}// 根据字符串创建处理器public static PaymentProcessor createProcessor(String type) {try {PaymentType paymentType = PaymentType.valueOf(type.toUpperCase());return createProcessor(paymentType);} catch (IllegalArgumentException e) {throw new IllegalArgumentException("Invalid payment type: " + type);}}
}
抽象工厂模式
// 抽象产品
public interface Button {void render();void onClick();
}public interface Checkbox {void render();void toggle();
}// Windows风格组件
public class WindowsButton implements Button {@Overridepublic void render() {System.out.println("Rendering Windows style button");}@Overridepublic void onClick() {System.out.println("Windows button clicked");}
}public class WindowsCheckbox implements Checkbox {@Overridepublic void render() {System.out.println("Rendering Windows style checkbox");}@Overridepublic void toggle() {System.out.println("Windows checkbox toggled");}
}// Mac风格组件
public class MacButton implements Button {@Overridepublic void render() {System.out.println("Rendering Mac style button");}@Overridepublic void onClick() {System.out.println("Mac button clicked");}
}public class MacCheckbox implements Checkbox {@Overridepublic void render() {System.out.println("Rendering Mac style checkbox");}@Overridepublic void toggle() {System.out.println("Mac checkbox toggled");}
}// 抽象工厂
public interface UIComponentFactory {Button createButton();Checkbox createCheckbox();
}// 具体工厂
public class WindowsUIFactory implements UIComponentFactory {@Overridepublic Button createButton() {return new WindowsButton();}@Overridepublic Checkbox createCheckbox() {return new WindowsCheckbox();}
}public class MacUIFactory implements UIComponentFactory {@Overridepublic Button createButton() {return new MacButton();}@Overridepublic Checkbox createCheckbox() {return new MacCheckbox();}
}// 工厂创建器
public class UIFactoryProvider {public static UIComponentFactory getFactory(String osType) {if ("Windows".equalsIgnoreCase(osType)) {return new WindowsUIFactory();} else if ("Mac".equalsIgnoreCase(osType)) {return new MacUIFactory();}throw new IllegalArgumentException("Unsupported OS type: " + osType);}
}
2.4 使用示例
public class ECommerceApplication {public static void main(String[] args) {// 支付处理示例processPayment();// UI组件创建示例createUIComponents();}private static void processPayment() {BigDecimal amount = new BigDecimal("99.99");// 使用不同的支付方式PaymentProcessor creditCardProcessor = PaymentProcessorFactory.createProcessor(PaymentProcessorFactory.PaymentType.CREDIT_CARD);creditCardProcessor.processPayment(amount, "1234567890123456");PaymentProcessor paypalProcessor = PaymentProcessorFactory.createProcessor("paypal");paypalProcessor.processPayment(amount, "user@example.com");}private static void createUIComponents() {String osType = System.getProperty("os.name");String factoryType = osType.toLowerCase().contains("win") ? "Windows" : "Mac";UIComponentFactory factory = UIFactoryProvider.getFactory(factoryType);Button button = factory.createButton();Checkbox checkbox = factory.createCheckbox();button.render();checkbox.render();}
}
2.5 优缺点分析
优点:
- 降低代码耦合度,符合开闭原则
- 易于扩展新的产品类型
- 隐藏了对象创建的复杂性
缺点:
- 增加了系统复杂性
- 需要额外的工厂类
三、观察者模式(Observer Pattern)
3.1 模式概述
观察者模式定义了对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式在事件处理、模型-视图架构中应用广泛。
3.2 实际应用场景
- 事件监听系统:GUI事件处理
- 模型-视图架构:数据变化时更新多个视图
- 消息通知系统:用户状态变化通知
- 股票价格监控:价格变化时通知所有关注者
3.3 Java实现方式
传统观察者模式
import java.util.*;// 观察者接口
public interface StockObserver {void update(String stockSymbol, BigDecimal price, BigDecimal change);
}// 被观察者接口
public interface StockSubject {void addObserver(StockObserver observer);void removeObserver(StockObserver observer);void notifyObservers();
}// 股票数据类
public class Stock {private String symbol;private BigDecimal price;private BigDecimal previousPrice;public Stock(String symbol, BigDecimal initialPrice) {this.symbol = symbol;this.price = initialPrice;this.previousPrice = initialPrice;}public void setPrice(BigDecimal newPrice) {this.previousPrice = this.price;this.price = newPrice;}public String getSymbol() { return symbol; }public BigDecimal getPrice() { return price; }public BigDecimal getChange() { return price.subtract(previousPrice); }
}// 股票市场类(被观察者)
public class StockMarket implements StockSubject {private final List<StockObserver> observers;private final Map<String, Stock> stocks;public StockMarket() {this.observers = new ArrayList<>();this.stocks = new HashMap<>();}@Overridepublic void addObserver(StockObserver observer) {observers.add(observer);}@Overridepublic void removeObserver(StockObserver observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (StockObserver observer : observers) {for (Stock stock : stocks.values()) {observer.update(stock.getSymbol(), stock.getPrice(), stock.getChange());}}}public void updateStockPrice(String symbol, BigDecimal newPrice) {Stock stock = stocks.get(symbol);if (stock == null) {stock = new Stock(symbol, newPrice);stocks.put(symbol, stock);} else {stock.setPrice(newPrice);}notifyObservers();}public void addStock(String symbol, BigDecimal initialPrice) {stocks.put(symbol, new Stock(symbol, initialPrice));}
}// 具体观察者实现
public class StockDisplay implements StockObserver {private final String displayName;public StockDisplay(String displayName) {this.displayName = displayName;}@Overridepublic void update(String stockSymbol, BigDecimal price, BigDecimal change) {String changeStr = change.compareTo(BigDecimal.ZERO) >= 0 ? "+" + change : change.toString();System.out.printf("[%s] %s: $%.2f (%s)%n", displayName, stockSymbol, price, changeStr);}
}public class AlertSystem implements StockObserver {private final Map<String, BigDecimal> alertThresholds;public AlertSystem() {this.alertThresholds = new HashMap<>();}public void setAlert(String stockSymbol, BigDecimal threshold) {alertThresholds.put(stockSymbol, threshold);}@Overridepublic void update(String stockSymbol, BigDecimal price, BigDecimal change) {BigDecimal threshold = alertThresholds.get(stockSymbol);if (threshold != null) {if (price.compareTo(threshold) >= 0) {System.out.printf("🚨 ALERT: %s has reached $%.2f (threshold: $%.2f)%n", stockSymbol, price, threshold);}}}
}public class TradingBot implements StockObserver {private final Map<String, TradingStrategy> strategies;public TradingBot() {this.strategies = new HashMap<>();}public void setStrategy(String stockSymbol, TradingStrategy strategy) {strategies.put(stockSymbol, strategy);}@Overridepublic void update(String stockSymbol, BigDecimal price, BigDecimal change) {TradingStrategy strategy = strategies.get(stockSymbol);if (strategy != null) {strategy.execute(stockSymbol, price, change);}}public interface TradingStrategy {void execute(String stockSymbol, BigDecimal price, BigDecimal change);}public static class SimpleMovingAverageStrategy implements TradingStrategy {@Overridepublic void execute(String stockSymbol, BigDecimal price, BigDecimal change) {if (change.compareTo(new BigDecimal("5")) > 0) {System.out.printf("🤖 Trading Bot: BUY signal for %s at $%.2f%n", stockSymbol, price);} else if (change.compareTo(new BigDecimal("-5")) < 0) {System.out.printf("🤖 Trading Bot: SELL signal for %s at $%.2f%n", stockSymbol, price);}}}
}
基于Java事件的现代实现
import java.util.concurrent.*;
import java.util.function.Consumer;// 事件类
public class StockPriceEvent {private final String symbol;private final BigDecimal price;private final BigDecimal change;private final LocalDateTime timestamp;public StockPriceEvent(String symbol, BigDecimal price, BigDecimal change) {this.symbol = symbol;this.price = price;this.change = change;this.timestamp = LocalDateTime.now();}// Getterspublic String getSymbol() { return symbol; }public BigDecimal getPrice() { return price; }public BigDecimal getChange() { return change; }public LocalDateTime getTimestamp() { return timestamp; }
}// 现代化的事件发布器
public class EventBus {private final Map<Class<?>, List<Consumer<?>>> subscribers;private final ExecutorService executorService;public EventBus() {this.subscribers = new ConcurrentHashMap<>();this.executorService = Executors.newCachedThreadPool();}@SuppressWarnings("unchecked")public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {subscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);}@SuppressWarnings("unchecked")public <T> void publish(T event) {List<Consumer<?>> handlers = subscribers.get(event.getClass());if (handlers != null) {for (Consumer<?> handler : handlers) {executorService.submit(() -> {try {((Consumer<T>) handler).accept(event);} catch (Exception e) {System.err.println("Error handling event: " + e.getMessage());}});}}}public void shutdown() {executorService.shutdown();}
}// 现代化的股票市场
public class ModernStockMarket {private final EventBus eventBus;private final Map<String, Stock> stocks;public ModernStockMarket(EventBus eventBus) {this.eventBus = eventBus;this.stocks = new ConcurrentHashMap<>();}public void updateStockPrice(String symbol, BigDecimal newPrice) {Stock stock = stocks.computeIfAbsent(symbol, k -> new Stock(k, newPrice));stock.setPrice(newPrice);StockPriceEvent event = new StockPriceEvent(symbol, newPrice, stock.getChange());eventBus.publish(event);}
}
3.4 使用示例
public class StockMarketDemo {public static void main(String[] args) {// 传统观察者模式示例traditionalObserverDemo();// 现代事件驱动示例modernEventDrivenDemo();}private static void traditionalObserverDemo() {System.out.println("=== 传统观察者模式示例 ===");StockMarket market = new StockMarket();// 创建观察者StockDisplay mainDisplay = new StockDisplay("Main Display");StockDisplay mobileDisplay = new StockDisplay("Mobile App");AlertSystem alertSystem = new AlertSystem();TradingBot tradingBot = new TradingBot();// 注册观察者market.addObserver(mainDisplay);market.addObserver(mobileDisplay);market.addObserver(alertSystem);market.addObserver(tradingBot);// 设置警报和交易策略alertSystem.setAlert("AAPL", new BigDecimal("150"));tradingBot.setStrategy("AAPL", new TradingBot.SimpleMovingAverageStrategy());// 模拟股价变化market.addStock("AAPL", new BigDecimal("145.50"));market.updateStockPrice("AAPL", new BigDecimal("147.25"));market.updateStockPrice("AAPL", new BigDecimal("152.80"));market.updateStockPrice("AAPL", new BigDecimal("148.90"));}private static void modernEventDrivenDemo() {System.out.println("\n=== 现代事件驱动示例 ===");EventBus eventBus = new EventBus();ModernStockMarket market = new ModernStockMarket(eventBus);// 订阅事件eventBus.subscribe(StockPriceEvent.class, event -> {System.out.printf("📱 Mobile Alert: %s is now $%.2f (change: %+.2f)%n",event.getSymbol(), event.getPrice(), event.getChange());});eventBus.subscribe(StockPriceEvent.class, event -> {if (event.getChange().abs().compareTo(new BigDecimal("3")) > 0) {System.out.printf("📧 Email Alert: Significant change in %s: %+.2f%n",event.getSymbol(), event.getChange());}});eventBus.subscribe(StockPriceEvent.class, event -> {System.out.printf("📊 Analytics: Recording %s price data at %s%n",event.getSymbol(), event.getTimestamp().format(DateTimeFormatter.ofPattern("HH:mm:ss")));});// 模拟股价更新market.updateStockPrice("GOOGL", new BigDecimal("2750.25"));market.updateStockPrice("GOOGL", new BigDecimal("2755.80"));market.updateStockPrice("GOOGL", new BigDecimal("2745.30"));// 清理资源eventBus.shutdown();}
}
3.5 优缺点分析
优点:
- 实现了松耦合,观察者和被观察者可以独立变化
- 支持动态添加和删除观察者
- 符合开闭原则,易于扩展
缺点:
- 如果观察者过多,通知所有观察者会耗费时间
- 可能造成内存泄漏(观察者没有及时移除)
- 调试复杂,观察者链可能很长
四、设计模式的最佳实践
4.1 选择合适的模式
在实际项目中选择设计模式时,需要考虑以下因素:
- 问题的复杂度:不要为了使用模式而使用模式
- 团队熟悉度:确保团队成员理解所选择的模式
- 项目规模:小项目可能不需要复杂的设计模式
- 性能要求:某些模式可能带来性能开销
4.2 组合使用模式
在实际项目中,多种设计模式往往会组合使用:
// 组合使用单例、工厂和观察者模式的示例
public class NotificationManager {private static volatile NotificationManager instance;private final EventBus eventBus;private final NotificationChannelFactory channelFactory;private NotificationManager() {this.eventBus = new EventBus();this.channelFactory = new NotificationChannelFactory();setupEventHandlers();}public static NotificationManager getInstance() {if (instance == null) {synchronized (NotificationManager.class) {if (instance == null) {instance = new NotificationManager();}}}return instance;}private void setupEventHandlers() {eventBus.subscribe(UserLoginEvent.class, this::handleUserLogin);eventBus.subscribe(OrderCompletedEvent.class, this::handleOrderCompleted);}private void handleUserLogin(UserLoginEvent event) {NotificationChannel channel = channelFactory.createChannel("email");channel.send("Welcome back!", event.getUserEmail());}private void handleOrderCompleted(OrderCompletedEvent event) {NotificationChannel smsChannel = channelFactory.createChannel("sms");NotificationChannel emailChannel = channelFactory.createChannel("email");String message = "Your order #" + event.getOrderId() + " has been completed!";smsChannel.send(message, event.getUserPhone());emailChannel.send(message, event.getUserEmail());}public void publishEvent(Object event) {eventBus.publish(event);}
}
4.3 避免过度设计
虽然设计模式很有用,但要避免过度设计:
- YAGNI原则:You Ain’t Gonna Need It - 不要实现当前不需要的功能
- 渐进式重构:从简单开始,需要时再引入设计模式
- 保持简洁:代码的可读性和维护性比炫技更重要
五、总结
设计模式是软件开发的重要工具,但它们不是银弹。在Java开发中,单例模式帮助我们管理全局资源,工厂模式让对象创建更加灵活,观察者模式实现了松耦合的事件处理。
成功应用设计模式的关键在于:
- 深入理解问题本质:选择最适合的解决方案
- 平衡复杂性和灵活性:不要过度设计
- 持续学习和实践:通过实际项目积累经验
- 团队协作:确保团队成员都理解所使用的模式