设计模式,单例和工厂模式

设计模式详解

一、什么是设计模式?

定义 :设计模式是在软件设计中,针对特定场景通用、可复用的解决方案 。它不是具体的代码,而是最佳实践的总结和指导思想

通俗理解:就像建筑领域的"户型图"或"结构模板",针对不同的居住需求(场景),有成熟的解决方案。


设计模式的三个层次:

  1. 创建型模式对象创建的优化方案

    • 单例模式、工厂模式、建造者模式、原型模式等

    • 解决"如何创建对象"的问题

  2. 结构型模式类和对象的组合方案

    • 适配器模式、装饰器模式、代理模式、组合模式等

    • 解决"如何组合类和对象"的问题

  3. 行为型模式对象间的通信协作方案

    • 观察者模式、策略模式、责任链模式、模板方法模式等

    • 解决"对象如何交互和分配职责"的问题


设计模式的六大原则(SOLID原则):

原则 含义 例子
单一职责 一个类只负责一项职责 UserService 只处理用户相关逻辑
开闭原则 对扩展开放,对修改关闭 通过接口新增实现,不改旧代码
里氏替换 子类可以替换父类 List list = new ArrayList()
接口隔离 建立专用接口,不建立庞大接口 多个小接口代替一个大接口
依赖倒置 依赖抽象,不依赖具体 依赖接口,不依赖具体实现类
迪米特法则 最少知道原则 只与直接朋友通信

二、单例模式(Singleton Pattern)

核心思想 :确保一个类只有一个实例,并提供全局访问点。

应用场景

  • 需要频繁创建销毁的对象

  • 工具类对象(配置读取、日志记录、线程池)

  • 重量级资源(数据库连接池、缓存管理器)

五种实现方式(从基础到高级)

1. 饿汉式(线程安全)

复制代码
public class Singleton1 {
    // 1. 私有构造
    private Singleton1() {}
    
    // 2. 静态实例,类加载时就创建
    private static final Singleton1 instance = new Singleton1();
    
    // 3. 全局访问点
    public static Singleton1 getInstance() {
        return instance;
    }
}

优点:线程安全,实现简单

缺点:类加载时就创建,可能浪费资源(如果从未使用)

2. 懒汉式(线程不安全)

复制代码
public class Singleton2 {
    private static Singleton2 instance;
    
    private Singleton2() {}
    
    // 有线程安全问题:多个线程可能同时创建多个实例
    public static Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}

3. 懒汉式(线程安全,方法同步)

复制代码
public class Singleton3 {
    private static Singleton3 instance;
    
    private Singleton3() {}
    
    // 同步整个方法,性能较差
    public static synchronized Singleton3 getInstance() {
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }
}

4. 双重检查锁(DCL,推荐)

复制代码
public class Singleton4 {
    // 使用 volatile 防止指令重排序
    private static volatile Singleton4 instance;
    
    private Singleton4() {}
    
    public static Singleton4 getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton4.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton4();
                }
            }
        }
        return instance;
    }
}

为什么用 volatile

  • 防止指令重排序:instance = new Singleton() 分3步:

    1. 分配内存空间

    2. 初始化对象

    3. 将instance指向分配的内存

  • 如果没有volatile,步骤2和3可能重排序,其他线程可能拿到未完全初始化的对象

5. 静态内部类(最优实现)

复制代码
public class Singleton5 {
    private Singleton5() {}
    
    // 静态内部类,在第一次使用时才会加载
    private static class SingletonHolder {
        private static final Singleton5 instance = new Singleton5();
    }
    
    public static Singleton5 getInstance() {
        return SingletonHolder.instance;
    }
}

优点

  • 线程安全(JVM保证类加载的线程安全)

  • 延迟加载(调用getInstance时才加载内部类)

  • 实现简单

6. 枚举单例(最安全)

复制代码
public enum Singleton6 {
    INSTANCE;  // 这就是单例实例
    
    public void doSomething() {
        // 业务方法
    }
}
// 使用:Singleton6.INSTANCE.doSomething();

优点

  • 绝对防止反射攻击

  • 绝对防止序列化问题

  • 线程安全

  • 实现最简单

推荐 :日常开发用静态内部类 ,需要绝对安全用枚举


三、工厂模式

核心思想将对象的创建和使用分离。定义一个创建对象的接口,让子类决定实例化哪个类。

三种工厂模式对比

类型 特点 适用场景
简单工厂 一个工厂类创建所有产品 产品种类少,变化不频繁
工厂方法 每个产品对应一个工厂 产品种类多,需要扩展
抽象工厂 创建产品族 需要创建相关或依赖的对象家族

1. 简单工厂模式

复制代码
// 1. 产品接口
interface Car {
    void run();
}

// 2. 具体产品
class Benz implements Car {
    public void run() { System.out.println("奔驰在跑"); }
}

class BMW implements Car {
    public void run() { System.out.println("宝马在跑"); }
}

// 3. 简单工厂
class CarFactory {
    public static Car createCar(String type) {
        if ("Benz".equals(type)) {
            return new Benz();
        } else if ("BMW".equals(type)) {
            return new BMW();
        }
        throw new IllegalArgumentException("未知汽车类型");
    }
}

// 使用
public class Client {
    public static void main(String[] args) {
        Car car = CarFactory.createCar("Benz");
        car.run();  // 输出:奔驰在跑
    }
}

缺点:新增产品需要修改工厂类,违反开闭原则


2. 工厂方法模式

复制代码
// 1. 产品接口
interface Car {
    void run();
}

// 2. 具体产品
class Benz implements Car {
    public void run() { System.out.println("奔驰在跑"); }
}

class BMW implements Car {
    public void run() { System.out.println("宝马在跑"); }
}

// 3. 工厂接口
interface CarFactory {
    Car createCar();
}

// 4. 具体工厂
class BenzFactory implements CarFactory {
    public Car createCar() {
        return new Benz();
    }
}

class BMWFactory implements CarFactory {
    public Car createCar() {
        return new BMW();
    }
}

// 使用
public class Client {
    public static void main(String[] args) {
        // 需要奔驰
        CarFactory benzFactory = new BenzFactory();
        Car benz = benzFactory.createCar();
        benz.run();
        
        // 需要宝马
        CarFactory bmwFactory = new BMWFactory();
        Car bmw = bmwFactory.createCar();
        bmw.run();
    }
}

优点:符合开闭原则,新增产品只需新增工厂


3. 抽象工厂模式

解决"产品族"创建问题:比如要创建一个"家庭套餐",包含汽车、沙发、电视等配套产品。

复制代码
// 1. 抽象产品族
interface Car {
    void run();
}

interface Sofa {
    void sit();
}

// 2. 具体产品族A:现代风格
class ModernCar implements Car {
    public void run() { System.out.println("现代汽车在跑"); }
}

class ModernSofa implements Sofa {
    public void sit() { System.out.println("坐在现代沙发上"); }
}

// 3. 具体产品族B:古典风格
class ClassicCar implements Car {
    public void run() { System.out.println("古典汽车在跑"); }
}

class ClassicSofa implements Sofa {
    public void sit() { System.out.println("坐在古典沙发上"); }
}

// 4. 抽象工厂
interface FurnitureFactory {
    Car createCar();
    Sofa createSofa();
}

// 5. 具体工厂
class ModernFactory implements FurnitureFactory {
    public Car createCar() { return new ModernCar(); }
    public Sofa createSofa() { return new ModernSofa(); }
}

class ClassicFactory implements FurnitureFactory {
    public Car createCar() { return new ClassicCar(); }
    public Sofa createSofa() { return new ClassicSofa(); }
}

// 使用
public class Client {
    public static void main(String[] args) {
        // 创建一套现代风格的家具
        FurnitureFactory modernFactory = new ModernFactory();
        Car modernCar = modernFactory.createCar();
        Sofa modernSofa = modernFactory.createSofa();
        modernCar.run();
        modernSofa.sit();
    }
}

四、面试回答要点

单例模式回答模板

单例模式确保一个类只有一个实例。我常用静态内部类 方式实现,它既线程安全又能延迟加载。如果需要绝对安全,会用枚举 方式,因为它能防止反射和序列化攻击。实现时要注意构造器私有静态实例变量全局访问点三个关键点。

工厂模式回答模板

工厂模式将对象的创建和使用分离,有三种形式:

  1. 简单工厂:一个工厂类创建所有产品,适合简单场景但违反开闭原则。

  2. 工厂方法:每个产品对应一个工厂,符合开闭原则,是Spring中BeanFactory的思想基础。

  3. 抽象工厂:创建产品族,适合需要创建一整套相关对象的场景。

工厂模式的核心价值解耦,让客户端不关心具体产品的创建细节。

实际应用场景

  • 单例模式:Spring的Bean默认是单例,MyBatis的SqlSessionFactory,日志工具Logger

  • 工厂方法:Spring的BeanFactory,Java的Calendar.getInstance()

  • 抽象工厂:JDBC连接工厂,可以创建Connection、Statement等一组相关对象

记住:设计模式是解决特定问题的工具,不要为了用模式而用模式,关键是理解其设计思想。

相关推荐
Qiuner17 小时前
Spring Boot 全局异常处理策略设计(三):@ExceptionHandler 与 @ControllerAdvice 生效原理源码解析
java·spring boot·后端
零度@17 小时前
Java 消息中间件 - RabbitMQ 全解(保姆级 2026)
java·rabbitmq·java-rabbitmq
u01040583617 小时前
企业微信自建应用权限模型与 RBAC 在 Spring Security 中的映射
java·spring·企业微信
墨雨晨曦8817 小时前
通过调用deepseek的api来实现智能客服
java
予枫的编程笔记17 小时前
Elasticsearch核心架构与基础原理:解密其极速性能的底层逻辑
java·大数据·人工智能·elasticsearch·搜索引擎·架构·全文检索
Seven9717 小时前
数据结构-图
java
Yu_iChan17 小时前
苍穹外卖Day09 地址簿模块
java·数据库·mybatis
Java天梯之路17 小时前
Spring Boot 钩子全集实战(五):ApplicationContextInitializer详解
java·spring boot·后端