本文主要归纳设计模式中的创建模式 (creational pattern),主要包括了工厂模式,单例多例,建造者模式,和原型模式等。

创建模式是指对象的实例化过程,这些模式都提供了一种将对象实例化从客户端(Client)分离的方法。

为什么要有创建模式,这也符合[[开闭原则]],Java 自带的 new Object() 实例化对象没有任何问题,问题在于修改,一旦当具体实例涉及变化,那么就必须修改实例自身,不符合开闭原则,所以才有这么多的创建模式。将对外暴露的接口抽象起来,将对象创建的方式封装,对外接口尽量减少。

为什么需要使用 Factory 设计模式

  • Creates objects without specifying the exact class to instantiate
  • Decoupling Object creation
    • client code doesn’t need to know the specific class of the object it creates
    • object creation logic is complex or requires dynamic decision-making
    • decouple the instantiation process from the client code for better flexibility and maintainability
    • 抽象和封装,封装对象创建过程,隐藏复杂的实例化
  • Reusability: Centralized object creation logic reduces redundancy
  • 客户端代码依赖于工厂接口而非具体类,减少了不同组件之间的紧密耦合。这使得在不影响客户端代码的情况下替换不同的实现变得更加容易。
    • Flexibility and Extensibility, enables your software to be modularly expandable. You can add new classes without changing the application code, the factory handles object creation while client code remains unchanged.

简单工厂

主要分为三个部分,工厂,抽象产品,具体产品。

工厂直接提供具体产品,也就是直接创建具体类。

工厂模式将产品的生产和其提供的功能隔离。

多态工厂方法

又称为工厂方法。

The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

分为四个部分,抽象工厂,具体工厂,抽象产品,具体产品。将产品实例化放到具体工厂子类中实现,抽象出工厂的一般方法。通常一个具体工厂产出一种具体产品。

因此在添加具体产品后不需要修改具体工厂类,而只需要相应的添加具体工厂来产出产品。

抽象工厂

和多态工厂相同,包含四个部分。

The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

抽象工厂工厂方法的区别在于,抽象工厂模式中,一个工厂可以提供多个抽象产品。在工厂方法模式中,由“具体工厂”决定提供哪一类具体产品;在抽象工厂中,由客户端决定返回哪一类产品。

抽象工厂解决的问题是,创建一组相关的对象,但是不指定其具体实现。

单例

类至始至终只有一个实例。

解决的问题

Ensures that a class has only one instance and provides a global access point to it.

Eager initialization

The instance of Singleton is created at the time of class loading.

public class EagerSingletion {
	private static final EagerSingletion instance = new EagerSingletion();
	private EagerSingletion() {}
	public static EagerSingletion getInstance() {
		return instance;
	}
}

在 Singleton 不占用太多资源时可以使用 static 在类加载时使用这个方法。但是对于大对象这个方法就不是很好。

Static block initialization

Use static block to create instance

public class StaticBlockSingleton {
	private static StaticBlockSingleton instance = null;
	private StaticBlockSingleton() {}
	static {
		try {
			instance = new StaticBlockSingleton();
		} catch(Exception e) {
			throw new RunTimeException("Exception creating singleton");
		}
	}

	public static StaticBlockSingleton getInstance() {
		return instance;
	}
}

使用 static 字段和 static block 定义的实例都是在实例对象被使用之前就已经创建。

Lazy Initialization

在第一次使用时创建。

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

但是这个方法在单线程中是没有问题的,如果是多线程同时调用 getInstance 方法,就可能创建多个实例。

Thread Safe Singleton

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

synchronized 作用于静态方法等同于对类加锁。

Double checked Singleton

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

Bill Pugh Singleton

使用内部静态类

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

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

这里涉及到类加载机制,在类加载 Singleton 时,内部静态类是不会被加载的。只有当有调用 getInstance() 方法,实例才会初始化。

多例

一个类存在多个自身的实例,并且多例类需要自己创建,管理自己的实例,并向外提供自己的实例。

常见的线程池,数据库连接池就是多例的使用案例。

建造者模式

当需要创建的对象,创建步骤比较复杂时,可以将对象的”创建”和”表示”分离。Builder 模式允许用户将复杂对象的创建封装,允许使用者多个步骤来构造对象。

建造者模式包括:导演,抽象建造者,具体建造者,和产品

解决的问题

Simplifies the creation of complex objects with many optional parameters.

使用场景

建造者模式的使用场景:

  • 对象内部结构复杂,比如包含不同零件的汽车类,比如一个复杂的邮件类
  • 对象内部属性有依赖,需要按照某一些顺序进行赋值
  • 对象创建过程依赖系统其他对象,而这些依赖的对象不易获取

优点:

  • 封装了内部构造细节
  • 隐藏了内部表示
  • 构造对象内部实现可以灵活改动,客户端只通过抽象接口来构造该对象

缺点:

  • 相较于工厂模式,客户端需要额外的知识来创建对象

原型模式

通过原型指明要创建的对象类型,然后通过该原型创造更多的对象实例。Java 中可以通过 clone() ,或者反序列化来实现。原型模式允许客户端在不知道对象具体内部细节的情况下创建实例。

一个比较具体的使用场景,比如要对比两个对象修改前后的差异,可以在修改前复制一个对象实例,然后修改之后对比。再比如一次比较重的数据库查询,可以缓存该对象,再下一次请求到来时直接返回该对象的复制。

解决的问题

Creates new objects by copying existing ones, reducing cost。

一些比较适合的场景:

  • 当创建一个实例比较昂贵或者复杂时

原型模式的优点:

  • 对客户端隐藏了构造对象的复杂细节
  • 对客户端提供了一种在不确定类型情况下创建对象实例的方法
  • 某一些场景下复制对象实例要比创建来的更有效率

缺点:

  • 对于一些只有少量对象的系统来说并不太需要
  • 每一个原型类的子类都需要实现 clone 方法,有些时候创建对象拷贝并不是非常轻松

reference