目录

引言:设计模式的哲学意义

一、单例模式:帝国的唯一君主

双重校验锁实现(线程安全)

二、工厂模式:创造万物的神之手

简单工厂模式(静态工厂)

三、观察者模式:消息帝国的烽火台

JDK内置实现(Observable类)

模式对比:三大模式的本质差异

结语:模式的艺术与哲学


设计模式如同武侠世界中的内功心法,看似无形却决定着代码的生死存亡。 本文将用实战代码+生活化案例,带你领略三大模式的精妙之处。

引言:设计模式的哲学意义

在软件工程领域,设计模式是解决特定问题的经典范式。如同建筑大师克里斯托弗·亚历山大在《建筑模式语言》中提出的253个模式,软件设计模式也提供了可复用的解决方案框架。本文将聚焦三种最常用的模式:单例(Singleton)、工厂(Factory)和观察者(Observer),通过代码实战揭示其内在智慧。

设计模式的核心价值:

  • 提高代码复用性:通过设计模式,可以复用成熟的解决方案,减少重复造轮子。
  • 增强代码可维护性:设计模式使得代码结构更加清晰,易于维护和修改。
  • 提升代码可扩展性:通过设计模式,系统更容易扩展和适应新的需求。

示例验证:设计模式的基本概念 

// 单例模式示例
// 定义一个公共类,类名为Logger
public class Logger {
    // 声明一个静态私有成员变量,用于存储Logger类的唯一实例
    private static Logger instance;
    
    // 私有构造方法:防止外部通过new关键字创建实例
    private Logger() {
        // 构造方法体(此处为空,表示无特殊初始化操作)
    }
    
    // 公共静态方法:获取Logger类的唯一实例(单例)
    public static Logger getInstance() {
        // 检查实例是否已被创建
        if (instance == null) {
            // 若未被创建,则新建一个Logger实例
            instance = new Logger();
        }
        // 返回已存在的实例(或新建的实例)
        return instance;
    }
    
    // 公共实例方法:记录日志信息
    public void log(String message) {
        // 将日志信息输出到控制台
        System.out.println("日志信息: " + message);
    }
}  // 类定义结束

一、单例模式:帝国的唯一君主

核心思想:确保类仅有一个实例,并提供全局访问点。适用于需要严格控制资源访问的场景(如数据库连接池、配置管理器)。

什么是单例模式?

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于需要全局唯一实例的场景,如日志记录器、配置管理器等。

单例模式的实现方式

  • 饿汉式:在类加载时就创建实例。
  • 懒汉式:在第一次调用时创建实例。
  • 双重校验锁:在多线程环境下安全地创建单例实例。
  • 静态内部类:利用静态内部类的特性来实现单例。
双重校验锁实现(线程安全)
// 皇帝类:实现线程安全的单例模式
public class Emperor {
    // 使用 volatile 关键字修饰静态实例变量:
    // 1. 保证多线程环境下的可见性(线程修改后立即同步到主内存)
    // 2. 禁止指令重排序(防止 new Emperor() 的构造过程被重排导致未初始化完成的对象被使用)
    private static volatile Emperor instance;
    
    // 私有化构造函数:
    // 1. 防止外部通过 new 关键字创建实例
    // 2. 确保单例控制权在类内部
    private Emperor() { 
        // 构造方法内部逻辑:模拟皇帝登基的唯一性
        System.out.println("登基大典!唯一皇帝诞生"); 
    }
    
    // 公共静态方法:获取全局唯一皇帝实例
    public static Emperor getInstance() {
        // 第一次检查(无需加锁):
        // 1. 若实例已存在则直接返回,避免不必要的同步开销
        // 2. 提升性能(约90%的调用无需进入同步块)
        if (instance == null) {
            // 同步代码块:锁定 Emperor.class 对象
            // 1. 确保同一时刻只有一个线程进入创建实例
            // 2. 防止多线程并发创建多个实例
            synchronized (Emperor.class) {
                // 第二次检查(关键步骤):
                // 1. 防止多个线程通过第一次检查后等待锁,导致重复创建
                // 2. 确保即使在并发环境下也仅创建一个实例
                if (instance == null) {
                    // 创建唯一实例(volatile 禁止此处指令重排)
                    instance = new Emperor();
                }
            }
        }
        // 返回已创建的实例(或新建的实例)
        return instance;
    }
    
    // 实例方法:皇帝行为示例
    public void rule() {
        // 模拟皇帝颁布法令
        System.out.println("皇帝颁布法令");
    }
}

// 示例使用类:演示单例模式调用
public class Kingdom {
    // 程序入口
    public static void main(String[] args) {
        // 获取第一个皇帝实例(触发构造函数)
        Emperor emperor1 = Emperor.getInstance();
        // 获取第二个皇帝实例(实际返回已创建的实例)
        Emperor emperor2 = Emperor.getInstance();
        
        // 验证两个引用指向同一对象(预期输出 true)
        System.out.println(emperor1 == emperor2); // true
        
        // 调用皇帝行为方法
        emperor1.rule();
    }
}

 枚举实现(最佳实践)

public enum Universe {
    INSTANCE;
    
    public void bigBang() {
        System.out.println("宇宙起源唯一奇点");
    }
}

// 调用方式
Universe.INSTANCE.bigBang();

应用场景

  1. 配置文件加载器(避免重复解析)

  2. 线程池管理器(统一资源调度)

  3. 缓存系统(全局唯一存储)

模式验证题

某系统需要全局唯一的ID生成器,要求:

  1. 多线程环境下线程安全

  2. 延迟初始化

  3. 防止反射攻击

请选择实现方案并说明原因。

答案:推荐枚举实现。枚举天然防反射攻击,JVM保证线程安全,且代码简洁。

二、工厂模式:创造万物的神之手

核心哲学封装对象创建过程,实现创建逻辑与使用逻辑的解耦。

1. 什么是工厂模式?

工厂模式是一种创建型设计模式,它提供了一种创建对象的接口,但让子类决定实例化哪一个类。工厂模式通常用于需要根据条件动态创建对象的场景。

2. 工厂模式的类型

  • 简单工厂模式:通过一个工厂类创建不同的对象。
  • 工厂方法模式:每个工厂类都有一个方法来创建对象。
  • 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族。
简单工厂模式(静态工厂)
// 定义武器接口,包含攻击行为
interface Weapon {
    // 声明攻击方法(接口方法默认为public abstract)
    void attack();
}

// 实现武器接口的具体长剑类
class Sword implements Weapon {
    // 重写攻击方法(使用@Override注解确保正确重写)
    @Override 
    // 实现长剑的攻击行为
    public void attack() { 
        // 输出长剑攻击效果
        System.out.println("长剑挥砍!"); 
    }
}

// 实现武器接口的具体弓箭类
class Bow implements Weapon {
    // 重写攻击方法
    @Override 
    // 实现弓箭的攻击行为
    public void attack() { 
        // 输出弓箭攻击效果
        System.out.println("弓箭射击!"); 
    }
}

// 武器工厂类(工厂模式实现)
class WeaponFactory {
    // 静态工厂方法:根据类型创建武器实例
    public static Weapon createWeapon(String type) {
        // 使用switch表达式根据武器类型创建对象
        return switch (type) {
            // 当类型为"sword"时创建长剑实例
            case "sword" -> new Sword();
            // 当类型为"bow"时创建弓箭实例
            case "bow" -> new Bow();
            // 处理不支持的类型
            default -> 
                // 抛出非法参数异常
                throw new IllegalArgumentException("未知武器");
        };
    }
}

// 战士类:演示武器使用
class Warrior {
    // 装备武器的方法
    public void equipWeapon(String type) {
        // 通过工厂创建武器实例
        Weapon weapon = WeaponFactory.createWeapon(type);
        // 使用武器进行攻击
        weapon.attack();
    }
}

 抽象工厂模式(产品族创建)

interface ArmyFactory {
    Warrior createWarrior();
    Mount createMount();
}

class RomanFactory implements ArmyFactory {
    @Override public Warrior createWarrior() { return new Legionnaire(); }
    @Override public Mount createMount() { return new Horse(); }
}

class MongolFactory implements ArmyFactory {
    @Override public Warrior createWarrior() { return new Archer(); }
    @Override public Mount createMount() { return new MongolianHorse(); }
}

应用场景

  1. JDBC连接创建(DriverManager.getConnection())

  2. Spring BeanFactory(IoC容器核心)

  3. 日志框架(LoggerFactory.getLogger())

模式验证题

现需实现跨平台UI组件库:

  • Windows风格:按钮 + 文本框

  • Mac风格:按钮 + 文本框

如何设计保证:

  1. 避免平台条件判断分散在代码各处

  2. 新增Linux风格时不影响已有代码

答案:采用抽象工厂模式,定义UIComponentFactory接口,派生出WindowsFactory、MacFactory等具体工厂。

三、观察者模式:消息帝国的烽火台

核心机制定义对象间的一对多依赖,当一个对象状态改变时,所有依赖它的对象自动更新。

1. 什么是观察者模式?

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会自动更新。观察者模式通常用于需要实时更新的场景,如股票价格监控系统。

2. 观察者模式的实现方式

  • 接口实现:定义观察者接口和主题接口。
  • 具体实现:实现观察者和主题的具体类。
JDK内置实现(Observable类)
// 导入Java内置观察者模式所需的类和接口
import java.util.Observable;  // 被观察对象的基类
import java.util.Observer;    // 观察者接口

// 新闻通讯社类(被观察者/主题),继承自Observable
class NewsAgency extends Observable {
    // 私有字段:存储最新新闻内容
    private String news;
    
    // 设置新闻的方法
    public void setNews(String news) {
        // 更新新闻内容
        this.news = news;
        // 调用继承的方法:标记状态已改变(必须调用才能触发通知)
        setChanged();  // 标记状态变更
        // 通知所有注册的观察者,传递新闻内容作为参数
        notifyObservers(news);  // 触发通知
    }  // 方法结束
}  // 类结束

// 订阅者类(观察者),实现Observer接口
class Subscriber implements Observer {
    // 订阅者名称字段
    private String name;
    
    // 带参数的构造方法:初始化订阅者名称
    public Subscriber(String name) { 
        // 为当前对象的name字段赋值
        this.name = name; 
    }  // 构造方法结束
    
    // 实现接口方法:当被观察对象发生变化时被调用
    @Override
    public void update(Observable o, Object arg) {
        // 打印接收到的新闻快讯
        // arg参数是被观察者传递给notifyObservers()的数据
        System.out.printf("%s 收到快讯: %s\n", name, arg);
    }  // 方法结束
}  // 类结束

// 演示观察者模式使用的示例类
public class NewsSystem {
    // 程序主入口方法
    public static void main(String[] args) {
        // 创建新闻通讯社实例(被观察者)
        NewsAgency agency = new NewsAgency();
        
        // 添加订阅者(观察者)到通讯社
        agency.addObserver(new Subscriber("张记者"));  // 创建并添加订阅者"张记者"
        agency.addObserver(new Subscriber("李主编"));  // 创建并添加订阅者"李主编"
        
        // 设置新新闻(自动通知所有观察者)
        agency.setNews("Java 21正式发布!"); // 自动通知所有观察者
    }  // 方法结束
}  // 类结束

 JavaBean事件模型

// 定义事件
class LoginEvent extends EventObject {
    private final String username;
    public LoginEvent(Object source, String username) {
        super(source);
        this.username = username;
    }
    public String getUsername() { return username; }
}

// 事件监听器接口
interface LoginListener extends EventListener {
    void onLoginSuccess(LoginEvent event);
}

// 事件源
class LoginService {
    private final List<LoginListener> listeners = new ArrayList<>();
    
    public void addLoginListener(LoginListener l) {
        listeners.add(l);
    }
    
    public void login(String username) {
        System.out.println(username + " 登录成功");
        fireLoginEvent(username);
    }
    
    private void fireLoginEvent(String username) {
        LoginEvent event = new LoginEvent(this, username);
        for (LoginListener l : listeners) {
            l.onLoginSuccess(event);
        }
    }
}

应用场景

  1. GUI事件处理(按钮点击监听)

  2. 消息中间件(发布/订阅模型)

  3. Spring事件驱动(ApplicationEvent)

模式验证题

实现电商库存预警系统:

  • 当库存低于阈值时
    ✓ 发送短信给采购经理
    ✓ 记录预警日志
    ✓ 更新Dashboard显示

如何设计保证新增通知渠道时,库存核心逻辑无需修改?

答案:采用观察者模式,库存系统作为Subject,短信服务、日志服务等作为Observer。


模式对比:三大模式的本质差异

模式 解决的核心问题 关系复杂度 典型应用
单例模式 实例数量控制 一对一 配置管理器、线程池
工厂模式 对象创建解耦 一对多(产品) Spring BeanFactory
观察者模式 状态变化通知 一对多(观察者) GUI事件系统

结语:模式的艺术与哲学

设计模式不是银弹,而是应对复杂性的思维工具。正如GoF在《设计模式》中所说:"模式是特定上下文中解决重现问题的方案"。掌握它们的关键在于:

  1. 理解场景:单例控制资源,工厂解耦创建,观察者处理通知

  2. 避免滥用:简单场景直接new对象比工厂更直接

  3. 组合创新:Spring框架通过组合工厂+单例实现IoC容器

终极思考:若将整个系统看作宇宙:

  • 单例是奇点(唯一存在)

  • 工厂是造物主(创造万物)

  • 观察者是引力波(传递状态变化)

在架构设计中,模式如同乐高积木的基础模块,程序员需要根据具体场景灵活组合。当你下次面对需求变更时,不妨思考:哪种模式能优雅地化解这场危机?


彩蛋:尝试用三种模式组合实现如下系统:

一个全局配置中心(单例)管理配置项
配置变更时(观察者)通知各服务
服务实例通过工厂创建

模式不是教条,而是通往优雅代码的地图。掌握它们,你将在软件设计的江湖中所向披靡。

通过本文的讲解,你已经掌握了Java设计模式中单例模式、工厂模式和观察者模式的实现与应用。在实际开发中,合理使用这些模式可以显著提升代码的复用性、可维护性和可扩展性。单例模式适用于需要全局唯一实例的场景,工厂模式适用于需要动态创建对象的场景,观察者模式适用于需要实时更新的场景。

实践建议:

  1. 在实际项目中根据需求选择合适的设计模式。
  2. 学习和探索更多的设计模式高级技巧,如组合模式和代理模式。
  3. 阅读和分析优秀的Java项目,学习如何在实际项目中应用这些模式。

希望这篇博客能够帮助你深入理解Java设计模式的实现与应用,提升你的开发效率和代码质量!如果你有任何问题或建议,欢迎在评论区留言!

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐