文章目录
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):实现了抽象产品接口,是工厂方法模式创建的目标。
三、优缺点
优点:
- 解耦:将产品的创建与使用分离,降低了客户端与具体产品之间的耦合度。
- 可扩展性:新增产品时,只需增加新的具体产品类和对应的工厂子类,无需修改原有代码,符合开闭原则。
- 灵活性:客户端可以根据需要选择不同的工厂子类来创建不同的产品实例。
缺点:
- 复杂性增加:每增加一个新产品,都需要增加一个具体产品类和一个对应的工厂子类,类的个数容易过多,增加系统复杂度。
- 理解难度增加:引入了抽象层,增加了系统的抽象性和理解难度。
- 系统开销:有更多的类需要编译和运行,可能会给系统带来一些额外的开销。
四、应用场景
工厂方法模式适用于以下场景:
- 当一个类不知道它所需要的对象的类时,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可。
- 当一个系统需要独立于它的产品的创建、构成和表示时,工厂方法模式使得产品的创建与具体的使用细节分离。
- 当需要引入新的产品而不影响现有的系统结构时,工厂方法模式提供了良好的扩展性。
五、示例
以下是一个简单的工厂方法模式的代码实例,假设我们有一个产品接口 Product
和两个实现该接口的具体产品类 ConcreteProductA
和 ConcreteProductB
。我们还有一个创建这些产品的工厂接口 Factory
和两个实现该接口的工厂类 ConcreteFactoryA
和 ConcreteFactoryB
。
// 产品接口 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()
。ConcreteProductA
和 ConcreteProductB
是实现了 Product
接口的具体产品类。Factory
是一个工厂接口,它定义了一个方法 createProduct()
,该方法用于创建 Product
对象。ConcreteFactoryA
和 ConcreteFactoryB
是实现了 Factory
接口的具体工厂类,它们分别负责创建 ConcreteProductA
和 ConcreteProductB
的实例。
在客户端代码 FactoryMethodDemo
中,我们分别通过 ConcreteFactoryA
和 ConcreteFactoryB
创建了 ConcreteProductA
和 ConcreteProductB
的实例,并调用了它们的 use()
方法。这样,我们就实现了在客户端代码中不直接依赖于具体产品的创建细节,而是通过工厂类来创建所需的产品对象。
六、总结
工厂方法模式是一种常用的设计模式,它通过定义工厂接口和具体工厂类来创建产品对象,实现了对创建过程的封装和隐藏,提高了系统的可扩展性和灵活性。然而,它也存在一些缺点,如增加系统复杂性和理解难度等。在实际应用中,需要根据具体场景和需求来选择是否使用工厂方法模式。