Java 中的设计模式:单例模式与工厂模式解析
Java 中的设计模式:单例模式与工厂模式解析
设计模式是软件开发中总结出的经典解决方案,它们能够提升代码的可读性、可维护性和可复用性。本文将深入探讨 Java 中的 单例模式(Singleton Pattern) 和 工厂模式(Factory Pattern),通过详细的代码实例和解析,帮助你更好地理解这两种常见的设计模式。
1. 单例模式(Singleton Pattern)
1.1 单例模式简介
单例模式确保一个类在整个应用程序生命周期内仅有一个实例,并提供一个全局访问点。常见的应用场景包括:
- 数据库连接管理
- 线程池
- 配置管理
- 日志管理
1.2 经典实现方式
1.2.1 饿汉式(线程安全,类加载时初始化)
public class SingletonEager {
// 类加载时就创建实例
private static final SingletonEager INSTANCE = new SingletonEager();
// 私有构造方法,防止外部实例化
private SingletonEager() {}
// 提供公共访问方法
public static SingletonEager getInstance() {
return INSTANCE;
}
}
优点: 线程安全,简单易实现
缺点: 类加载时就创建实例,可能会浪费内存
1.2.2 懒汉式(线程不安全,按需初始化)
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {}
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
优点: 按需初始化,节省内存
缺点: 线程不安全,可能导致多个实例的创建
1.2.3 线程安全的懒汉式(同步方法)
public class SingletonSynchronized {
private static SingletonSynchronized instance;
private SingletonSynchronized() {}
public static synchronized SingletonSynchronized getInstance() {
if (instance == null) {
instance = new SingletonSynchronized();
}
return instance;
}
}
优点: 线程安全
缺点: 每次获取实例都需要同步,影响性能
1.2.4 双重检查锁(推荐使用)
public class SingletonDoubleChecked {
private static volatile SingletonDoubleChecked instance;
private SingletonDoubleChecked() {}
public static SingletonDoubleChecked getInstance() {
if (instance == null) {
synchronized (SingletonDoubleChecked.class) {
if (instance == null) {
instance = new SingletonDoubleChecked();
}
}
}
return instance;
}
}
优点: 线程安全,性能较高
缺点: 需要 volatile
关键字,确保可见性
1.2.5 静态内部类(推荐)
public class SingletonHolder {
private SingletonHolder() {}
private static class Holder {
private static final SingletonHolder INSTANCE = new SingletonHolder();
}
public static SingletonHolder getInstance() {
return Holder.INSTANCE;
}
}
优点: 线程安全,懒加载,推荐使用
缺点: 依赖类加载机制,可能在某些场景下不适用
2. 工厂模式(Factory Pattern)
2.1 工厂模式简介
工厂模式是一种创建型设计模式,它通过封装对象创建逻辑,避免在代码中直接使用 new
关键字,提高代码的灵活性和可维护性。
常见的工厂模式包括:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
2.2 简单工厂模式(静态工厂模式)
适用于创建类型较少、变化不大的对象。
// 产品接口
interface Product {
void use();
}
// 具体产品A
class ProductA implements Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品B
class ProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
// 简单工厂类
class SimpleFactory {
public static Product createProduct(String type) {
switch (type) {
case "A": return new ProductA();
case "B": return new ProductB();
default: throw new IllegalArgumentException("未知产品类型");
}
}
}
// 测试
public class SimpleFactoryTest {
public static void main(String[] args) {
Product product = SimpleFactory.createProduct("A");
product.use();
}
}
优点: 简单易用,封装对象创建
缺点: 不符合开闭原则,新增产品需修改工厂代码
2.3 工厂方法模式(面向接口)
适用于扩展性强的场景,每个产品有独立的工厂。
// 产品接口
interface Product {
void use();
}
// 具体产品A
class ProductA implements Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品B
class ProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
// 工厂接口
interface Factory {
Product createProduct();
}
// 具体工厂A
class FactoryA implements Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
// 具体工厂B
class FactoryB implements Factory {
@Override
public Product createProduct() {
return new ProductB();
}
}
// 测试
public class FactoryMethodTest {
public static void main(String[] args) {
Factory factory = new FactoryA();
Product product = factory.createProduct();
product.use();
}
}
优点: 遵循开闭原则,新产品只需新增工厂
缺点: 增加系统复杂性,每个产品需创建一个工厂类
2.4 抽象工厂模式(多种产品族)
适用于需要创建一系列相关产品的场景。
// 抽象产品接口
interface CPU {
void compute();
}
// 具体产品
class IntelCPU implements CPU {
@Override
public void compute() {
System.out.println("Intel CPU 计算");
}
}
class AMDCPU implements CPU {
@Override
public void compute() {
System.out.println("AMD CPU 计算");
}
}
// 抽象工厂
interface CPUFactory {
CPU createCPU();
}
// 具体工厂
class IntelFactory implements CPUFactory {
@Override
public CPU createCPU() {
return new IntelCPU();
}
}
class AMDFactory implements CPUFactory {
@Override
public CPU createCPU() {
return new AMDCPU();
}
}
// 测试
public class AbstractFactoryTest {
public static void main(String[] args) {
CPUFactory factory = new IntelFactory();
CPU cpu = factory.createCPU();
cpu.compute();
}
}
优点: 支持多产品族创建,遵循开闭原则
缺点: 结构复杂,适用于大规模系统
3. 单例模式与工厂模式的结合
在实际开发中,单例模式和工厂模式并不是孤立存在的,而是可以结合使用的。例如,我们可以使用工厂模式来管理单例对象的创建,从而增强代码的灵活性。
3.1 结合单例模式的工厂模式
在某些情况下,我们希望某个对象在全局范围内只存在一个实例(单例模式),但仍然希望通过工厂模式来管理对象的创建。这可以通过 工厂方法模式 + 单例模式 组合实现。
3.1.1 示例:单例模式的数据库连接工厂
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
// 数据库连接管理器(单例模式)
class DatabaseConnection {
private static volatile DatabaseConnection instance;
private Connection connection;
private DatabaseConnection() {
try {
// 这里使用 SQLite,实际项目可改为 MySQL、PostgreSQL 等
String url = "jdbc:sqlite:sample.db";
connection = DriverManager.getConnection(url);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
public Connection getConnection() {
return connection;
}
}
// 工厂类
class ConnectionFactory {
public static Connection getConnection() {
return DatabaseConnection.getInstance().getConnection();
}
}
// 测试
public class SingletonFactoryTest {
public static void main(String[] args) {
Connection conn1 = ConnectionFactory.getConnection();
Connection conn2 = ConnectionFactory.getConnection();
System.out.println(conn1 == conn2); // true,证明是同一个实例
}
}
3.2 结合工厂模式的单例模式
有时候,我们的单例类内部可能需要管理多个子类实例,这种情况下,工厂模式可以帮助我们 动态创建和管理不同的实例。
3.2.1 示例:日志管理器
// 日志接口
interface Logger {
void log(String message);
}
// 具体日志实现:控制台日志
class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("ConsoleLogger: " + message);
}
}
// 具体日志实现:文件日志
class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("FileLogger: " + message);
// 实际开发中,这里应写入文件
}
}
// 日志工厂(结合单例模式)
class LoggerFactory {
private static final LoggerFactory instance = new LoggerFactory();
private Logger logger;
private LoggerFactory() {}
public static LoggerFactory getInstance() {
return instance;
}
public Logger getLogger(String type) {
if (logger == null) {
switch (type) {
case "console":
logger = new ConsoleLogger();
break;
case "file":
logger = new FileLogger();
break;
default:
throw new IllegalArgumentException("未知的日志类型");
}
}
return logger;
}
}
// 测试
public class SingletonFactoryLoggerTest {
public static void main(String[] args) {
Logger logger1 = LoggerFactory.getInstance().getLogger("console");
Logger logger2 = LoggerFactory.getInstance().getLogger("console");
logger1.log("Hello, Singleton Factory!");
System.out.println(logger1 == logger2); // true,证明是同一个实例
}
}
4. 线程安全问题与性能优化
4.1 单例模式的线程安全问题
在高并发环境下,懒汉式单例 存在线程安全问题,例如:
public class UnsafeSingleton {
private static UnsafeSingleton instance;
private UnsafeSingleton() {}
public static UnsafeSingleton getInstance() {
if (instance == null) {
instance = new UnsafeSingleton(); // 可能多个线程同时进入此处
}
return instance;
}
}
改进方案:
- 同步方法(但性能低)
- 双重检查锁
- 静态内部类
4.2 工厂模式的线程安全问题
如果工厂类是无状态的(如 SimpleFactory
),则 通常不需要 额外的同步控制。但如果工厂类涉及到 对象的缓存或复用,就需要考虑线程安全问题。
例如,在工厂中维护一个 对象缓存池:
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
// 线程安全的工厂
class ThreadSafeFactory {
private static final Map<String, Product> productCache = new ConcurrentHashMap<>();
public static Product getProduct(String type) {
return productCache.computeIfAbsent(type, k -> {
switch (k) {
case "A": return new ProductA();
case "B": return new ProductB();
default: throw new IllegalArgumentException("未知产品类型");
}
});
}
}
这样可以确保工厂不会重复创建相同的对象,同时 ConcurrentHashMap 保证了线程安全。
5. 单例模式与工厂模式的适用场景
5.1 何时使用单例模式?
- 全局共享的资源(如数据库连接池、线程池、配置管理)
- 需要确保唯一实例的对象(如
Runtime.getRuntime()
)
5.2 何时使用工厂模式?
- 创建逻辑复杂 的对象(如
Connection
、Thread
) - 需要解耦对象创建 和使用(如
Spring BeanFactory
) - 希望增强代码的扩展性(如新增
ProductC
时无需修改已有代码)
5.3 何时结合使用?
- 既想确保对象唯一性(单例模式),又想提供灵活的创建方式(工厂模式)
- 例如:数据库连接池、日志管理器、线程池
6. 进一步扩展:结合反射与枚举
6.1 反射破坏单例模式
即使我们使用 私有构造方法,反射仍然可以破坏单例模式:
import java.lang.reflect.Constructor;
public class ReflectionAttack {
public static void main(String[] args) throws Exception {
SingletonHolder instance1 = SingletonHolder.getInstance();
Constructor<SingletonHolder> constructor = SingletonHolder.class.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonHolder instance2 = constructor.newInstance();
System.out.println(instance1 == instance2); // false,实例被破坏!
}
}
解决方案: 在构造方法中防止重复创建:
private SingletonHolder() {
if (Holder.INSTANCE != null) {
throw new RuntimeException("禁止反射创建实例");
}
}
6.2 使用枚举实现单例模式(最安全)
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("枚举单例");
}
}
优点:
- 天然防止反射攻击
- 天然线程安全
- 易于序列化
至此,我们深入探讨了 Java 设计模式中的单例模式与工厂模式,并结合代码示例演示了多种应用场景。你是否有更好的实现方式?欢迎讨论!🚀
- 点赞
- 收藏
- 关注作者
评论(0)