363 lines
12 KiB
Markdown
363 lines
12 KiB
Markdown
# 设计模式
|
||
|
||
## 单例模式
|
||
|
||
### 懒汉模式
|
||
|
||
#### 双重检查锁定(DCL)
|
||
|
||
```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;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 静态内部类方式
|
||
|
||
利用 **静态内部类**(饿汉式)的特性来实现懒汉模式,并且可以保证线程安全。这种方式只有在调用 `getInstance()` 时才会加载内部类,从而实现懒加载。
|
||
|
||
```java
|
||
public class Singleton {
|
||
private Singleton() {
|
||
// 构造函数私有化
|
||
}
|
||
|
||
private static class SingletonHolder {
|
||
private static final Singleton INSTANCE = new Singleton();
|
||
}
|
||
|
||
public static Singleton getInstance() {
|
||
return SingletonHolder.INSTANCE;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 适用场景
|
||
|
||
- 如果单例对象比较重,且只在程序中某些特定情况下才使用,懒汉模式可以带来性能优势。
|
||
- 在低并发环境或单线程环境下,懒汉模式使用较为简单,不会有多线程安全问题。
|
||
- 对于高并发且需要频繁访问单例的场景,推荐使用**静态内部类**或**饿汉模式**来避免懒汉模式可能带来的性能问题。
|
||
|
||
#### 总结
|
||
|
||
懒汉模式有时为了延迟初始化而牺牲了性能,特别是在多线程环境下。为了保证线程安全,可以采用双重检查锁定(DCL)或静态内部类的方式,而**静态内部类方式是最推荐的方式**,它避免了同步开销,同时也能确保线程安全。
|
||
|
||
- **优点**:
|
||
1. 延迟加载,节省资源。
|
||
2. 内存优化,只有在需要时才实例化。
|
||
3. 减少不必要的初始化开销。
|
||
- **缺点**:
|
||
1. 传统懒汉模式线程不安全(多个线程可能会创建多个实例)。
|
||
2. 使用同步机制会有性能开销,特别是在多线程环境下。
|
||
3. 代码复杂性较高,容易引入错误。
|
||
4. 不适合高并发场景,特别是频繁访问时会影响性能。
|
||
|
||
### 饿汉模式
|
||
|
||
**饿汉模式**(Eager Initialization)是单例模式的一种实现方式,在该方式中,单例实例在类加载时就已经创建完成,而不是在首次使用时才创建。换句话说,**饿汉模式**的单例实例在类被加载时就会立即初始化,确保了单例模式的唯一性。
|
||
|
||
```java
|
||
public class Singleton {
|
||
|
||
// 在类加载时就初始化单例对象
|
||
private static final Singleton INSTANCE = new Singleton();
|
||
|
||
// 私有构造函数,防止外部通过 new 创建实例
|
||
private Singleton() {
|
||
}
|
||
|
||
// 提供全局访问点
|
||
public static Singleton getInstance() {
|
||
return INSTANCE;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 适用场景
|
||
|
||
- **简单应用**:适合没有延迟初始化需求的小型应用,或者单例对象的创建非常轻量,不会影响系统性能的场景。
|
||
- **实例化不耗费太多资源**:如果单例对象的初始化过程非常轻量级且无须延迟初始化,可以使用饿汉模式。
|
||
- **类加载顺序固定**:如果不在乎单例对象在启动时是否初始化,且单例对象的初始化过程没有严重性能损失,可以选择饿汉模式。
|
||
|
||
#### 总结
|
||
|
||
- **优点**:简单、线程安全、性能高(没有锁机制)、实现方便。
|
||
- **缺点**:不支持延迟加载、可能会浪费资源(如果单例对象不常用时)。
|
||
|
||
饿汉模式是单例模式中最简单的实现方式,适用于大多数情况下单例对象的创建开销较小,且不需要延迟初始化的场景。如果单例对象的创建非常轻量且不依赖于后期加载时的资源,饿汉模式是一个不错的选择。
|
||
|
||
### 枚举类型实现
|
||
|
||
**枚举类型实现单例模式**(Enum Singleton)是实现单例模式的另一种方式,优点是更加简洁、可靠,并且避免了许多常见的单例实现方式(如懒汉模式和饿汉模式)可能出现的问题。使用枚举类型的单例实现方式自 **Java 5** 引入以来,已经成为了最推荐的单例模式实现方式之一。
|
||
|
||
**当你不需要懒加载或延迟初始化时,使用枚举实现单例模式是最理想的选择。**
|
||
|
||
```java
|
||
public enum Singleton {
|
||
|
||
// 唯一实例
|
||
INSTANCE;
|
||
|
||
// 可以添加其他的方法
|
||
public void someMethod() {
|
||
System.out.println("This is a singleton instance method.");
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用示例
|
||
|
||
```java
|
||
public class Main {
|
||
public static void main(String[] args) {
|
||
// 获取单例对象
|
||
Singleton singleton = Singleton.INSTANCE;
|
||
|
||
// 调用单例方法
|
||
singleton.someMethod();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 总结
|
||
|
||
枚举类型实现单例模式是非常推荐的做法,尤其是在 Java 5 及以后的版本。它简单、线程安全、避免了反射和序列化带来的问题,且保证了单例模式的正确性。对于绝大多数的单例模式实现需求,枚举类型是最安全、最简洁、最优雅的解决方案。
|
||
|
||
## 工厂模式
|
||
|
||
工厂模式(Factory Pattern)是创建型设计模式的一种,它提供了一种创建对象的方式,而不需要暴露具体的类实例化过程。工厂模式有三种常见的实现方式:
|
||
|
||
### 1. **简单工厂模式(Simple Factory Pattern)**
|
||
|
||
简单工厂模式是工厂模式的一种简单形式,**它并不属于设计模式中的结构**,它是通过一个工厂类来决定创建哪一个产品类的实例。
|
||
|
||
- 特点
|
||
- 由工厂类来决定实例化哪个产品类,不暴露具体的产品创建过程。
|
||
- 客户端不需要知道产品类的具体信息,减少了与产品类的耦合。
|
||
- 优缺点
|
||
- 优点:客户端不需要关心对象的创建细节,降低了代码的耦合度。
|
||
- 缺点:工厂类需要维护一个 `if` 或 `switch` 来判断创建哪种产品,如果产品种类增加,工厂类会变得非常庞大且难以维护。
|
||
|
||
**示例**:
|
||
|
||
```java
|
||
class Product {
|
||
void use() {
|
||
System.out.println("Using the product");
|
||
}
|
||
}
|
||
|
||
class ConcreteProductA extends Product {
|
||
@Override
|
||
void use() {
|
||
System.out.println("Using Product A");
|
||
}
|
||
}
|
||
|
||
class ConcreteProductB extends Product {
|
||
@Override
|
||
void use() {
|
||
System.out.println("Using Product B");
|
||
}
|
||
}
|
||
|
||
class SimpleFactory {
|
||
public Product createProduct(String type) {
|
||
if (type.equals("A")) {
|
||
return new ConcreteProductA();
|
||
} else if (type.equals("B")) {
|
||
return new ConcreteProductB();
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
|
||
public class Client {
|
||
public static void main(String[] args) {
|
||
SimpleFactory factory = new SimpleFactory();
|
||
Product product = factory.createProduct("A");
|
||
product.use();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. **工厂方法模式(Factory Method Pattern)**
|
||
|
||
工厂方法模式是工厂模式的核心。它通过定义一个接口来创建对象,由子类来决定实例化哪一个类。工厂方法模式将简单工厂中的创建产品的逻辑分散到了各个具体的工厂类中。
|
||
|
||
- 特点
|
||
- 定义一个创建对象的接口,但让子类来决定具体实例化哪一个产品。
|
||
- 通过继承和多态来解决简单工厂的缺点,避免了在一个类中维护大量的 `if` 或 `switch` 语句。
|
||
- 优缺点
|
||
- 优点:符合开闭原则,增加新产品时只需要添加新的工厂类,不需要修改现有代码。
|
||
- 缺点:增加了类的数量,系统更为复杂。
|
||
|
||
**示例**:
|
||
|
||
```java
|
||
interface Product {
|
||
void use();
|
||
}
|
||
|
||
class ConcreteProductA implements Product {
|
||
@Override
|
||
public void use() {
|
||
System.out.println("Using Product A");
|
||
}
|
||
}
|
||
|
||
class ConcreteProductB implements Product {
|
||
@Override
|
||
public void use() {
|
||
System.out.println("Using Product B");
|
||
}
|
||
}
|
||
|
||
abstract class Creator {
|
||
public abstract Product factoryMethod();
|
||
}
|
||
|
||
class ConcreteCreatorA extends Creator {
|
||
@Override
|
||
public Product factoryMethod() {
|
||
return new ConcreteProductA();
|
||
}
|
||
}
|
||
|
||
class ConcreteCreatorB extends Creator {
|
||
@Override
|
||
public Product factoryMethod() {
|
||
return new ConcreteProductB();
|
||
}
|
||
}
|
||
|
||
public class Client {
|
||
public static void main(String[] args) {
|
||
Creator creatorA = new ConcreteCreatorA();
|
||
Product productA = creatorA.factoryMethod();
|
||
productA.use();
|
||
|
||
Creator creatorB = new ConcreteCreatorB();
|
||
Product productB = creatorB.factoryMethod();
|
||
productB.use();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. **抽象工厂模式(Abstract Factory Pattern)**
|
||
|
||
抽象工厂模式是工厂模式的进一步扩展,它提供了一个接口,用于创建一组相关或相互依赖的对象,而无需指定具体的类。与工厂方法模式不同,抽象工厂模式更侧重于一组产品的创建,而不是单个产品的创建。
|
||
|
||
- 特点
|
||
- 提供一个接口,用来创建相关或依赖的多个产品。
|
||
- 客户端通过抽象工厂来获取一系列的产品对象,而无需知道具体的实现。
|
||
- 优缺点
|
||
- 优点:可以方便地更换产品系列(产品的族),符合开闭原则。
|
||
- 缺点:增加了系统的复杂性,如果新增产品族或产品种类时需要修改现有的代码。
|
||
|
||
**示例**:
|
||
|
||
```java
|
||
interface ProductA {
|
||
void useA();
|
||
}
|
||
|
||
interface ProductB {
|
||
void useB();
|
||
}
|
||
|
||
class ConcreteProductA1 implements ProductA {
|
||
@Override
|
||
public void useA() {
|
||
System.out.println("Using Product A1");
|
||
}
|
||
}
|
||
|
||
class ConcreteProductB1 implements ProductB {
|
||
@Override
|
||
public void useB() {
|
||
System.out.println("Using Product B1");
|
||
}
|
||
}
|
||
|
||
class ConcreteProductA2 implements ProductA {
|
||
@Override
|
||
public void useA() {
|
||
System.out.println("Using Product A2");
|
||
}
|
||
}
|
||
|
||
class ConcreteProductB2 implements ProductB {
|
||
@Override
|
||
public void useB() {
|
||
System.out.println("Using Product B2");
|
||
}
|
||
}
|
||
|
||
interface AbstractFactory {
|
||
ProductA createProductA();
|
||
ProductB createProductB();
|
||
}
|
||
|
||
class ConcreteFactory1 implements AbstractFactory {
|
||
@Override
|
||
public ProductA createProductA() {
|
||
return new ConcreteProductA1();
|
||
}
|
||
|
||
@Override
|
||
public ProductB createProductB() {
|
||
return new ConcreteProductB1();
|
||
}
|
||
}
|
||
|
||
class ConcreteFactory2 implements AbstractFactory {
|
||
@Override
|
||
public ProductA createProductA() {
|
||
return new ConcreteProductA2();
|
||
}
|
||
|
||
@Override
|
||
public ProductB createProductB() {
|
||
return new ConcreteProductB2();
|
||
}
|
||
}
|
||
|
||
public class Client {
|
||
public static void main(String[] args) {
|
||
AbstractFactory factory1 = new ConcreteFactory1();
|
||
ProductA productA1 = factory1.createProductA();
|
||
ProductB productB1 = factory1.createProductB();
|
||
productA1.useA();
|
||
productB1.useB();
|
||
|
||
AbstractFactory factory2 = new ConcreteFactory2();
|
||
ProductA productA2 = factory2.createProductA();
|
||
ProductB productB2 = factory2.createProductB();
|
||
productA2.useA();
|
||
productB2.useB();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 总结:
|
||
|
||
- **简单工厂模式**:由工厂类根据不同的条件决定创建不同的对象,适合产品种类少且变化不大时。
|
||
- **工厂方法模式**:通过子类来决定产品的创建,适合产品种类较多时,且产品可能会变化。
|
||
- **抽象工厂模式**:提供多个相关的产品的创建接口,适合需要创建一系列相关产品的场景。 |