单例模式与工厂模式

文章目录

1. 单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,其目的是确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。单例模式广泛应用于需要控制资源访问的场合,比如配置文件读取、数据库连接、线程池等。

实现单例模式的几种方式

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

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

缺点:多线程环境下不安全。

2. 懒汉式(线程安全)

java 复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

缺点 :效率低下,每次调用getInstance()时都需要进行线程锁定

3. 双重检查锁定(Double-Checked Locking)

java 复制代码
public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

注意 :使用该结构可以确保只在第一次实例化时进行加锁操作,既保证了线程安全又提高了效率。这里使用了volatile关键字来确保多线程环境下的内存可见性(可参考文章线程安全,这里不再赘述)和禁止指令重排序(如下图)。

4. 静态内部类

java 复制代码
public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点:延迟加载,且线程安全。

说明

  • 由于静态内部类在外部类被加载时并不会被实例化,因此其内部实现的单例(通常是私有的静态成员变量)的初始化也是延迟的,直到第一次被访问时才会发生。这种延迟加载的机制有助于减少程序启动时的初始化开销。
  • 由于静态内部类的初始化是由JVM保证的,并且JVM在类加载时采用了同步机制(尽管这种同步对程序员是透明的),这确保了静态内部类的初始化过程是线程安全的。即,在JVM层面,它确保了在多线程环境下,静态内部类只会被初始化一次。

5. 枚举方式

java 复制代码
public enum Singleton {
    INSTANCE;

    public void whateverMethod() {
    }
}

优点:简洁,自动支持序列化机制,绝对防止多次实例化。

总结

选择哪种单例模式实现方式,取决于具体的应用场景和性能要求。
如果不需要延迟加载,可以优先考虑枚举方式,因为它不仅代码简洁,而且提供了自动的序列化机制,能够防止多次实例化。
如果需要延迟加载,且对性能要求较高,可以考虑使用静态内部类方式。
双重检查锁定方式适用于对性能要求不是非常苛刻,但需要保证线程安全的场景。
而简单的懒汉式和线程安全的懒汉式,则分别适用于单线程和多线程但不需要频繁访问单例的场景

2. 工厂模式

* 简单工厂模式

简单工厂模式(Simple Factory Pattern)是工厂模式的一种基础形式,尽管它并不属于GOF(Gang of Four)提出的23种设计模式之一,但它在面向对象的程序设计中扮演着重要的角色。以下是对简单工厂模式的详细解析:

一、定义

  • 定义:简单工厂模式属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式。它通过一个单独的工厂类来决定创建哪一种产品类的实例,并且这个工厂类会包含必要的判断逻辑,根据客户端的请求来创建对应的产品实例。

二、角色与职责

  • 工厂(Factory)角色:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类通常包含多个静态方法,每个方法对应一个产品类的创建逻辑。
  • 抽象产品(Abstract Product)角色:定义了一个产品的接口,具体产品必须实现这个接口。它是简单工厂模式创建的所有对象的父类,描述了所有实例所共有的公共接口。
  • 具体产品(Concrete Product)角色:实现了抽象产品接口的具体类,是简单工厂模式的创建目标。每个具体产品都充当抽象产品的某个具体类的实例。

三、适用场景

  • 当需要创建的对象较少,且创建过程相对简单时,可以使用简单工厂模式。
  • 当系统中产品类少且不会增加,或者一个具体工厂类负责创建的产品较少且不会增加时,也适合使用简单工厂模式。
  • 客户端不关心创建对象的细节,只关心类型和使用对象时,简单工厂模式能够很好地满足这一需求。

四、优缺点

  • 优点
    • 客户端只需要传入正确的参数,就可以获取到所需的对象,无需知道具体的创建细节。
    • 工厂类中包含必要的判断逻辑,可以根据当前的参数创建对应的产品实例,客户端可以免除直接创建产品对象的责任。
    • 实现了对创建实例和使用实例的责任分割,提高了代码的灵活性和可维护性
  • 缺点
    • 工厂类职责过重,如果要增加新的产品,需要修改工厂类的判断逻辑,这违背了开闭原则(即对扩展开放,对修改关闭)
    • 如果系统中产品类非常多,那么工厂类的代码量会非常大,增加了系统的复杂性

五、代码示例

以下是一个简单的代码示例,演示了如何使用简单工厂模式来创建不同类型的运算对象(加法、减法、乘法、除法):

java 复制代码
// 抽象产品类
interface Operation {
    double getResult();
}

// 具体产品类(加法)
class AddOperation implements Operation {
    private double number1;
    private double number2;

    public AddOperation(double number1, double number2) {
        this.number1 = number1;
        this.number2 = number2;
    }

    @Override
    public double getResult() {
        return number1 + number2;
    }
}

// 其他具体产品类(减法、乘法、除法)...

// 工厂类
class OperationFactory {
    public static Operation createOperation(String sign, double number1, double number2) {
        switch (sign) {
            case "+":
                return new AddOperation(number1, number2);
            // 其他case(减法、乘法、除法)...
            default:
                throw new IllegalArgumentException("Unsupported operation");
        }
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Operation operation = OperationFactory.createOperation("+", 1, 2);
        System.out.println(operation.getResult()); // 输出:3
    }
}

UML类图:

在这个示例中,Operation是抽象产品类,定义了运算的接口;AddOperation是具体产品类之一,实现了加法运算;OperationFactory是工厂类,负责根据传入的运算符来创建对应的运算对象。客户端代码通过调用工厂类的静态方法来获取运算对象,并调用其getResult方法来执行运算。

*工厂方法模式

工厂方法模式(Factory Method Pattern)是计算机网络技术中一种常用的类创建型设计模式。其核心精神在于封装类中变化的部分,提取其中个性化、善变的部分为独立类。通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。以下是工厂方法模式的详细解析:

一、定义与特点

  • 定义:工厂方法模式定义了一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的具体创建,而是成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口。

二、核心结构与角色

工厂方法模式的核心结构包含四个角色:

  • 抽象工厂(Abstract Factory):定义了一个创建产品的接口,但不负责具体产品的创建。
  • 具体工厂(Concrete Factory):实现了抽象工厂接口,负责创建具体的产品实例。
  • 抽象产品(Abstract Product):定义了产品的公共接口,是所有具体产品的父类。
  • 具体产品(Concrete Product):实现了抽象产品接口,是工厂方法模式创建的目标。

三、优缺点

优点

  1. 解耦:将产品的创建与使用分离,降低了客户端与具体产品之间的耦合度。
  2. 可扩展性:新增产品时,只需增加新的具体产品类和对应的工厂子类,无需修改原有代码,符合开闭原则。
  3. 灵活性:客户端可以根据需要选择不同的工厂子类来创建不同的产品实例。

缺点

  1. 复杂性增加:每增加一个新产品,都需要增加一个具体产品类和一个对应的工厂子类,类的个数容易过多,增加系统复杂度。
  2. 理解难度增加:引入了抽象层,增加了系统的抽象性和理解难度。
  3. 系统开销:有更多的类需要编译和运行,可能会给系统带来一些额外的开销。

四、应用场景

工厂方法模式适用于以下场景:

  1. 当一个类不知道它所需要的对象的类时,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可。
  2. 当一个系统需要独立于它的产品的创建、构成和表示时,工厂方法模式使得产品的创建与具体的使用细节分离。
  3. 当需要引入新的产品而不影响现有的系统结构时,工厂方法模式提供了良好的扩展性。

五、示例

以下是一个简单的工厂方法模式的代码实例,假设我们有一个产品接口 Product 和两个实现该接口的具体产品类 ConcreteProductAConcreteProductB。我们还有一个创建这些产品的工厂接口 Factory 和两个实现该接口的工厂类 ConcreteFactoryAConcreteFactoryB

java 复制代码
// 产品接口
interface Product {
    void use();
}

// 具体产品A
class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using ConcreteProductA");
    }
}

// 具体产品B
class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using ConcreteProductB");
    }
}

// 工厂接口
interface Factory {
    Product createProduct();
}

// 具体工厂A
class ConcreteFactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具体工厂B
class ConcreteFactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 客户端代码
public class FactoryMethodDemo {
    public static void main(String[] args) {
        // 使用工厂A创建产品A
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.use(); // 输出: Using ConcreteProductA

        // 使用工厂B创建产品B
        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.use(); // 输出: Using ConcreteProductB
    }
}

UML类图

在这个例子中,Product 是一个产品接口,它有一个方法 use()ConcreteProductAConcreteProductB 是实现了 Product 接口的具体产品类。Factory 是一个工厂接口,它定义了一个方法 createProduct(),该方法用于创建 Product 对象。ConcreteFactoryAConcreteFactoryB 是实现了 Factory 接口的具体工厂类,它们分别负责创建 ConcreteProductAConcreteProductB 的实例。

在客户端代码 FactoryMethodDemo 中,我们分别通过 ConcreteFactoryAConcreteFactoryB 创建了 ConcreteProductAConcreteProductB 的实例,并调用了它们的 use() 方法。这样,我们就实现了在客户端代码中不直接依赖于具体产品的创建细节,而是通过工厂类来创建所需的产品对象。

六、总结

工厂方法模式是一种常用的设计模式,它通过定义工厂接口和具体工厂类来创建产品对象,实现了对创建过程的封装和隐藏,提高了系统的可扩展性和灵活性。然而,它也存在一些缺点,如增加系统复杂性和理解难度等。在实际应用中,需要根据具体场景和需求来选择是否使用工厂方法模式。

相关推荐
WaaTong5 小时前
《重学Java设计模式》之 单例模式
java·单例模式·设计模式
C++忠实粉丝2 天前
Linux系统基础-多线程超详细讲解(5)_单例模式与线程池
linux·运维·服务器·c++·算法·单例模式·职场和发展
FANGhelloworld3 天前
C++面向对象设计模式——单例模式
c++·单例模式·设计模式
土了个豆子的3 天前
单例模式的概念和用处
单例模式
飞升不如收破烂~3 天前
在Spring框架中,容器管理的bean可以有不同的作用域(scope),其中最常用的两种是单例(singleton)和原型(prototype)。
spring·单例模式·原型模式
晨曦丿4 天前
设计模式之——单例模式
c++·单例模式·设计模式
徐子童5 天前
多线程案例---单例模式
java·单例模式·设计模式
Good_tea_h5 天前
线程安全的单例模式(Singleton)。
单例模式
Tang Paofan5 天前
C++单例模式
java·c++·单例模式
nice666606 天前
DAO模式及单例模式
java·数据库·sql·mysql·单例模式·idea