Java中的设计模式:单例模式、工厂模式、观察者模式的实现与应用
设计模式不是银弹,而是应对复杂性的思维工具。正如GoF在《设计模式》中所说:"模式是特定上下文中解决重现问题的方案理解场景:单例控制资源,工厂解耦创建,观察者处理通知避免滥用:简单场景直接new对象比工厂更直接组合创新:Spring框架通过组合工厂+单例实现IoC容器终极思考:若将整个系统看作宇宙:单例是奇点(唯一存在)工厂是造物主(创造万物)观察者是引力波(传递状态变化)在架构设计中,模式如同乐
目录
设计模式如同武侠世界中的内功心法,看似无形却决定着代码的生死存亡。 本文将用实战代码+生活化案例,带你领略三大模式的精妙之处。
引言:设计模式的哲学意义
在软件工程领域,设计模式是解决特定问题的经典范式。如同建筑大师克里斯托弗·亚历山大在《建筑模式语言》中提出的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();
应用场景:
-
配置文件加载器(避免重复解析)
-
线程池管理器(统一资源调度)
-
缓存系统(全局唯一存储)
模式验证题:
某系统需要全局唯一的ID生成器,要求:
多线程环境下线程安全
延迟初始化
防止反射攻击
请选择实现方案并说明原因。
答案:推荐枚举实现。枚举天然防反射攻击,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(); }
}
应用场景:
-
JDBC连接创建(DriverManager.getConnection())
-
Spring BeanFactory(IoC容器核心)
-
日志框架(LoggerFactory.getLogger())
模式验证题:
现需实现跨平台UI组件库:
Windows风格:按钮 + 文本框
Mac风格:按钮 + 文本框
如何设计保证:
避免平台条件判断分散在代码各处
新增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);
}
}
}
应用场景:
-
GUI事件处理(按钮点击监听)
-
消息中间件(发布/订阅模型)
-
Spring事件驱动(ApplicationEvent)
模式验证题:
实现电商库存预警系统:
当库存低于阈值时
✓ 发送短信给采购经理
✓ 记录预警日志
✓ 更新Dashboard显示如何设计保证新增通知渠道时,库存核心逻辑无需修改?
答案:采用观察者模式,库存系统作为Subject,短信服务、日志服务等作为Observer。
模式对比:三大模式的本质差异
模式 | 解决的核心问题 | 关系复杂度 | 典型应用 |
---|---|---|---|
单例模式 | 实例数量控制 | 一对一 | 配置管理器、线程池 |
工厂模式 | 对象创建解耦 | 一对多(产品) | Spring BeanFactory |
观察者模式 | 状态变化通知 | 一对多(观察者) | GUI事件系统 |
结语:模式的艺术与哲学
设计模式不是银弹,而是应对复杂性的思维工具。正如GoF在《设计模式》中所说:"模式是特定上下文中解决重现问题的方案"。掌握它们的关键在于:
-
理解场景:单例控制资源,工厂解耦创建,观察者处理通知
-
避免滥用:简单场景直接
new
对象比工厂更直接 -
组合创新:Spring框架通过组合工厂+单例实现IoC容器
终极思考:若将整个系统看作宇宙:
单例是奇点(唯一存在)
工厂是造物主(创造万物)
观察者是引力波(传递状态变化)
在架构设计中,模式如同乐高积木的基础模块,程序员需要根据具体场景灵活组合。当你下次面对需求变更时,不妨思考:哪种模式能优雅地化解这场危机?
彩蛋:尝试用三种模式组合实现如下系统:
一个全局配置中心(单例)管理配置项
配置变更时(观察者)通知各服务
服务实例通过工厂创建
模式不是教条,而是通往优雅代码的地图。掌握它们,你将在软件设计的江湖中所向披靡。
通过本文的讲解,你已经掌握了Java设计模式中单例模式、工厂模式和观察者模式的实现与应用。在实际开发中,合理使用这些模式可以显著提升代码的复用性、可维护性和可扩展性。单例模式适用于需要全局唯一实例的场景,工厂模式适用于需要动态创建对象的场景,观察者模式适用于需要实时更新的场景。
实践建议:
- 在实际项目中根据需求选择合适的设计模式。
- 学习和探索更多的设计模式高级技巧,如组合模式和代理模式。
- 阅读和分析优秀的Java项目,学习如何在实际项目中应用这些模式。
希望这篇博客能够帮助你深入理解Java设计模式的实现与应用,提升你的开发效率和代码质量!如果你有任何问题或建议,欢迎在评论区留言!
更多推荐
所有评论(0)