单例模式与工厂模式

avatar
作者
筋斗云
阅读量:0

文章目录

1. 单例模式

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

实现单例模式的几种方式

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

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

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

2. 懒汉式(线程安全)

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)

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. 静态内部类

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. 枚举方式

public enum Singleton {     INSTANCE;      public void whateverMethod() {     } } 

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

总结

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

2. 工厂模式

* 简单工厂模式

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

一、定义

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

二、角色与职责

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

三、适用场景

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

四、优缺点

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

五、代码示例

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

// 抽象产品类 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

// 产品接口 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() 方法。这样,我们就实现了在客户端代码中不直接依赖于具体产品的创建细节,而是通过工厂类来创建所需的产品对象。

六、总结

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

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!