第67章

Java设计模式

深入学习Java中的常用设计模式:单例模式、工厂模式、观察者模式、策略模式等经典设计模式的原理、实现和应用场景

学习目标

设计模式概述

什么是设计模式?

设计模式是在软件设计中常见问题的典型解决方案。它们就像预先制作的蓝图,可以定制来解决代码中反复出现的设计问题。设计模式不是具体的代码,而是解决特定问题的通用概念。

设计模式的优势

设计模式分类

创建型模式

处理对象创建机制,试图根据实际情况使用合适的方式创建对象。

  • 单例模式(Singleton)
  • 工厂模式(Factory)
  • 抽象工厂模式(Abstract Factory)
  • 建造者模式(Builder)
  • 原型模式(Prototype)

结构型模式

处理对象和类的组合,通过继承和组合来组成更大的结构。

  • 适配器模式(Adapter)
  • 装饰器模式(Decorator)
  • 外观模式(Facade)
  • 代理模式(Proxy)
  • 组合模式(Composite)

行为型模式

关注对象之间的通信和职责分配,处理算法和对象间的责任分配。

  • 观察者模式(Observer)
  • 策略模式(Strategy)
  • 命令模式(Command)
  • 状态模式(State)
  • 模板方法模式(Template Method)

单例模式(Singleton Pattern)

单例模式定义

单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式常用于需要控制资源访问的场景,如数据库连接池、日志记录器、配置管理器等。

饿汉式单例

饿汉式单例在类加载时就创建实例,线程安全但可能造成内存浪费。

EagerSingleton.java - 饿汉式单例实现
/**
 * 饿汉式单例模式
 * 
 * 特点:
 * - 类加载时就创建实例
 * - 线程安全
 * - 可能造成内存浪费
 */
public class EagerSingleton {
    
    // 在类加载时就创建实例
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    /**
     * 私有构造函数,防止外部实例化
     */
    private EagerSingleton() {
        System.out.println("EagerSingleton实例被创建");
    }
    
    /**
     * 获取单例实例
     * 
     * @return 单例实例
     */
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
    
    /**
     * 业务方法示例
     */
    public void doSomething() {
        System.out.println("EagerSingleton正在执行业务逻辑...");
    }
}

懒汉式单例

懒汉式单例延迟创建实例,需要时才创建,但需要考虑线程安全问题。

LazySingleton.java - 懒汉式单例实现(线程安全)
/**
 * 懒汉式单例模式(线程安全版本)
 * 
 * 特点:
 * - 延迟加载,需要时才创建实例
 * - 使用synchronized保证线程安全
 * - 性能相对较低
 */
public class LazySingleton {
    
    // 静态实例变量
    private static LazySingleton instance;
    
    /**
     * 私有构造函数
     */
    private LazySingleton() {
        System.out.println("LazySingleton实例被创建");
    }
    
    /**
     * 获取单例实例(线程安全)
     * 
     * @return 单例实例
     */
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
    
    /**
     * 业务方法示例
     */
    public void doSomething() {
        System.out.println("LazySingleton正在执行业务逻辑...");
    }
}

双重检查锁定

双重检查锁定(Double-Checked Locking)是懒汉式的优化版本,减少了同步开销。

DoubleCheckedSingleton.java - 双重检查锁定实现
/**
 * 双重检查锁定单例模式
 * 
 * 特点:
 * - 延迟加载
 * - 线程安全
 * - 性能较好
 */
public class DoubleCheckedSingleton {
    
    // 使用volatile确保多线程环境下的可见性
    private static volatile DoubleCheckedSingleton instance;
    
    /**
     * 私有构造函数
     */
    private DoubleCheckedSingleton() {
        System.out.println("DoubleCheckedSingleton实例被创建");
    }
    
    /**
     * 获取单例实例(双重检查锁定)
     * 
     * @return 单例实例
     */
    public static DoubleCheckedSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckedSingleton();
                }
            }
        }
        return instance;
    }
    
    /**
     * 业务方法示例
     */
    public void doSomething() {
        System.out.println("DoubleCheckedSingleton正在执行业务逻辑...");
    }
}
💻 查看完整代码 - 在线IDE体验

工厂模式(Factory Pattern)

工厂模式定义

工厂模式提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

简单工厂实现

Shape.java - 形状接口
/**
 * 形状接口
 */
public interface Shape {
    /**
     * 绘制形状
     */
    void draw();
}
Circle.java - 圆形实现
/**
 * 圆形类
 */
public class Circle implements Shape {
    
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}
ShapeFactory.java - 形状工厂
/**
 * 形状工厂类
 */
public class ShapeFactory {
    
    /**
     * 根据形状类型创建对应的形状对象
     * 
     * @param shapeType 形状类型
     * @return 形状对象
     */
    public static Shape createShape(String shapeType) {
        if (shapeType == null || shapeType.isEmpty()) {
            return null;
        }
        
        switch (shapeType.toUpperCase()) {
            case "CIRCLE":
                return new Circle();
            case "RECTANGLE":
                return new Rectangle();
            case "TRIANGLE":
                return new Triangle();
            default:
                throw new IllegalArgumentException("未知的形状类型: " + shapeType);
        }
    }
}

观察者模式(Observer Pattern)

观察者模式定义

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式实现

Observer.java - 观察者接口
/**
 * 观察者接口
 */
public interface Observer {
    /**
     * 更新方法,当被观察者状态改变时调用
     * 
     * @param news 新闻内容
     */
    void update(String news);
}
Subject.java - 被观察者接口
/**
 * 被观察者接口(主题)
 */
public interface Subject {
    /**
     * 添加观察者
     * 
     * @param observer 观察者
     */
    void addObserver(Observer observer);
    
    /**
     * 移除观察者
     * 
     * @param observer 观察者
     */
    void removeObserver(Observer observer);
    
    /**
     * 通知所有观察者
     * 
     * @param news 新闻内容
     */
    void notifyObservers(String news);
}
NewsAgency.java - 新闻机构(具体被观察者)
import java.util.ArrayList;
import java.util.List;

/**
 * 新闻机构类(具体的被观察者)
 */
public class NewsAgency implements Subject {
    
    private List observers = new ArrayList<>();
    private String latestNews;
    
    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
        System.out.println("添加了一个新的观察者");
    }
    
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
        System.out.println("移除了一个观察者");
    }
    
    @Override
    public void notifyObservers(String news) {
        System.out.println("通知所有观察者: " + news);
        for (Observer observer : observers) {
            observer.update(news);
        }
    }
    
    /**
     * 发布新闻
     * 
     * @param news 新闻内容
     */
    public void publishNews(String news) {
        this.latestNews = news;
        notifyObservers(news);
    }
}

策略模式(Strategy Pattern)

策略模式定义

策略模式定义了一系列算法,把它们一个个封装起来,并且使它们可相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。

策略模式实现

PaymentStrategy.java - 支付策略接口
/**
 * 支付策略接口
 */
public interface PaymentStrategy {
    /**
     * 执行支付
     * 
     * @param amount 支付金额
     */
    void pay(double amount);
}
CreditCardPayment.java - 信用卡支付策略
/**
 * 信用卡支付策略
 */
public class CreditCardPayment implements PaymentStrategy {
    
    private String cardNumber;
    private String cardHolder;
    
    public CreditCardPayment(String cardNumber, String cardHolder) {
        this.cardNumber = cardNumber;
        this.cardHolder = cardHolder;
    }
    
    @Override
    public void pay(double amount) {
        System.out.println(String.format("使用信用卡支付 %.2f 元", amount));
        System.out.println("卡号: " + maskCardNumber(cardNumber));
        System.out.println("持卡人: " + cardHolder);
    }
    
    private String maskCardNumber(String cardNumber) {
        return cardNumber.substring(0, 4) + " **** **** " + 
               cardNumber.substring(cardNumber.length() - 4);
    }
}
PaymentContext.java - 支付上下文
/**
 * 支付上下文类
 */
public class PaymentContext {
    
    private PaymentStrategy paymentStrategy;
    
    /**
     * 设置支付策略
     * 
     * @param paymentStrategy 支付策略
     */
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    
    /**
     * 执行支付
     * 
     * @param amount 支付金额
     */
    public void executePayment(double amount) {
        if (paymentStrategy == null) {
            throw new IllegalStateException("支付策略未设置");
        }
        paymentStrategy.pay(amount);
    }
}

设计模式最佳实践

使用设计模式的建议

  • 不要过度设计:只在真正需要时使用设计模式
  • 理解问题本质:先理解要解决的问题,再选择合适的模式
  • 保持简单:优先选择简单的解决方案
  • 考虑维护性:设计模式应该让代码更易维护,而不是更复杂
  • 团队共识:确保团队成员都理解所使用的设计模式

常见误区

  • 为了使用设计模式而使用,导致过度设计
  • 不理解模式的适用场景,盲目套用
  • 忽视性能影响,过度抽象
  • 不考虑团队成员的理解能力

本章小结