创建型模式

一、概述

创建型模式(Creational Pattern)关注对象的创建过程,在系统开发中应用非常广泛
这种模式对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离,对用户隐藏了类的实力的创建细节,这样会带来如下好处:

  • 无须关心创建对象的细节
  • 降低系统的耦合度
  • 让设计方案更易于修改和扩展

而每一种创建型模式都会基于下列三点:

  • 创建什么(What)
  • 由谁创建(Who)
  • 何时创建(When)

创建型模式共有六种:

设计原则名称 定义 使用频率
简单工厂模式 (Simple Factory Pattern) 定义一个工程类,它可以根据参数的不同,返回不同类的实例,被创建的实例通常都具有共同的父类 ⭐⭐⭐
工厂方法模式 (Factory Method Pattern) 定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类 ⭐⭐⭐⭐⭐
抽象工厂模式 (Abstract Factory Pattern) 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类 ⭐⭐⭐⭐⭐
建造者模式 (Builder Pattern) 将一个复杂对象的构造与它的表示分离,使得同样的构造过程可以创建不同的表示 ⭐⭐
原型模式 (Prototype Pattern) 使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象 ⭐⭐⭐
单例模式 (Singleton Pattern) 确保一个类只有一个实例,并提供一个全局的访问点来访问这个唯一实例 ⭐⭐⭐⭐

二、简单工厂模式

实现一个简单的加减乘除计算器

1. 创建什么

1.1 Operation

1
2
3
4
5
6
7
8
public abstract class Operation {

public double result;

public double getResult(double num1, double num2) {
return 0;
}
}

1.2 Add

1
2
3
4
5
6
7
public class Add extends Operation {
@Override
public double getResult(double num1, double num2) {
result = num1 + num2;
return result;
}
}

1.3 Sub

1
2
3
4
5
6
7
public class Sub extends Operation{
@Override
public double getResult(double num1, double num2) {
result = num1 - num2;
return result;
}
}

1.4 Mul

1
2
3
4
5
6
7
public class Mul extends Operation{
@Override
public double getResult(double num1, double num2) {
result = num1 * num2;
return result;
}
}

1.5 Div

1
2
3
4
5
6
7
public class Div extends Operation {
@Override
public double getResult(double num1, double num2) {
result = num1 / num2;
return result;
}
}

2. 由谁创建

2.1 OperationFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class OperationFactory {

private Operation operation;

public OperationFactory(Operation operation) {
this.operation = operation;
}

public static Operation createOperation(String in) {
switch (in) {
case "+":
return new add();
case "-":
return new sub();
case "*":
return new mul();
case "/":
return new div();
default:
return null;
}
}
}

3. 何时创建

3.1 createOperation方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Operation createOperation(String in) {
switch (in) {
case "+":
return new add();
case "-":
return new sub();
case "*":
return new mul();
case "/":
return new div();
default:
return null;
}
}

4. 客户端代码

1
2
3
4
5
6
Operation operation = OperationFactory.createOperation("/");
if (operation != null) {
System.out.println(operation.getResult(3, 0));
} else {
System.out.println("不正常输入!");
}

5. 优缺点及适用场景

5.1 优点

  • 无须知道所创建的具体产品的类名,只需要知道对应的参数即可

5.2 缺点

  • 职责过重,工厂类集中了所有产品的创建逻辑
  • 增加了系统的复杂度和理解难度
  • 系统扩展困难,一旦需要添加新产品就不得不修改工厂逻辑,一定程度上违背了开闭原则

5.3 适用场景

  • 工厂类负责创建的对象比较少,这样就可以避免工厂方法中的业务逻辑过于复杂
  • 客户端只知道工厂类的参数,对如何创建对象并不关心

三、工厂方法模式

输出数据图形,曲线图(LineDiagram)创建器生成曲线图,柱形图(ColumnDiagram)创建器生成柱形图

1. 创建什么

1.1 Diagram

1
2
3
public interface Diagram {
void create();
}

1.2 LineDiagram

1
2
3
4
5
6
public class LineDiagram implements Diagram {
@Override
public void create() {
System.out.println("生成曲线图");
}
}

1.3 ColumnDiagram

1
2
3
4
5
6
public class ColumnDiagram implements Diagram {
@Override
public void create() {
System.out.println("生成柱形图");
}
}

2. 由谁创建

2.1 DiagramFactroy

1
2
3
public interface DiagramFactory {
Diagram createDiagram();
}

2.2 LineDiagramFactory

1
2
3
4
5
6
public class LineDiagramFactory implements DiagramFactory {
@Override
public Diagram createDiagram() {
return new LineDiagram();
}
}

2.3 ColumnDiagramFactory

1
2
3
4
5
6
public class ColumnDiagramFactory implements DiagramFactory {
@Override
public Diagram createDiagram() {
return new ColumnDiagram();
}
}

3.何时创建

具体工厂中实现了抽象工厂的createDiagram()方法

4.客户端代码

1
2
3
4
5
6
7
8
9
DiagramFactory diagramFactory;                                  
Diagram diagram;
try {
diagramFactory = new LineDiagramFactory(); // 可引入配置文件和反射机制实现
diagram = diagramFactory.createDiagram();
diagram.create();
} catch (Exception e) {
e.printStackTrace();
}

5.优缺点及适用场景

5.1 优点

  • 解耦。用户只关心产品对应的工厂,无须关心实现细节,甚至无须知道具体产品的类名
  • 可扩展性,完全符合开闭原则。例如添加新产品时,无须修改抽象产品和抽象工厂的方法

5.2 缺点

  • 复杂度较高。添加新产品时既需要添加新产品的具体类也要添加新工厂的具体类
  • 有一定的抽象性和理解难度。因为考虑到可扩展性,引入了抽象层,在客户端代码中也需要通过抽象层进行定义

5.3 适用场景

  • 客户端不需要他所需要的对象的类
  • 抽象工厂类通过其子类来指定创建哪个对象

四、抽象工厂模式

某系统为了改进数据库操作的性能,自定义数据为连接对象(Connection)和语句(Statement)对象,可针对不同类型的数据库提供不同的连接对象和语句对象,如提供Oracle或MySQL专用连接类和语句类,而且用户可以通过配置文件等方式根据实际需要动态更换系统数据库。

1.创建什么

产品族:

  • Mysql 相关
  • Oracle 相关

产品等级结构:

  • 连接对象 Connection
  • 语句对象 Statement

1.1 Connection

1
2
3
public interface Connection {
void getConnection();
}

1.2 MysqlConnection

1
2
3
4
5
6
public class MysqlConnection implements Connection {
@Override
public void getConnection() {
System.out.println("Mysql连接对象");
}
}

1.3 OracleConnection

1
2
3
4
5
6
public class OracleConnection implements Connection {
@Override
public void getConnection() {
System.out.println("Oracle连接对象");
}
}

1.4 Statement

1
2
3
public interface Statement {
void getStatement();
}

1.5 MysqlStatement

1
2
3
4
5
6
public class MysqlStatement implements Statement {
@Override
public void getStatement() {
System.out.println("Mysql语句对象");
}
}

1.6 OracleStatement

1
2
3
4
5
6
public class OracleStatement implements Statement {
@Override
public void getStatement() {
System.out.println("Oracle语句对象");
}
}

2. 由谁创建

2.1 DataBaseFactory

1
2
3
4
public interface DataBaseFactory {
Connection getConnection();
Statement getStatement();
}

2.2 MysqlDataBaseFactory

1
2
3
4
5
6
7
8
9
10
11
public class MysqlDataBaseFactory implements DataBaseFactory {
@Override
public Connection getConnection() {
return new MysqlConnection();
}

@Override
public Statement getStatement() {
return new MysqlStatement();
}
}

2.3 OracleDataBaseFactory

1
2
3
4
5
6
7
8
9
10
11
public class OracleDataBaseFactory implements DataBaseFactory{
@Override
public Connection getConnection() {
return new OracleConnection();
}

@Override
public Statement getStatement() {
return new OracleStatement();
}
}

3. 何时创建

由确定的工厂创造确定的产品

4. 客户端代码

1
2
3
4
5
DataBaseFactory dataBaseFactory = new MysqlDataBaseFactory(); // 可引入配置文件和反射机制实现
Connection connection = dataBaseFactory.getConnection();
Statement statement = dataBaseFactory.getStatement();
connection.getConnection();
statement.getStatement();

5. 优缺点及适用场景

5.1 优点

  • 解耦。用户只关心产品对应的工厂,无须关心实现细节,甚至无须知道具体产品的类名
  • 可扩展性,完全符合开闭原则。例如添加新的产品族时,无须修改抽象产品和抽象工厂的方法

5.2 缺点

  • 起初设计时难度较大,需要考虑周全。添加新的产品等级结构时,需要修改抽象工厂的方法,违背了开闭原则。例如在此基础上新增产品等级结构对象 Exception,那么对应的产品族 Mysql、Oracle 均需要新增 MysqlException、OracleException,并且基类 DataBaseFactory 也需要新增方法 getException,同时其子了 MysqlDataBaseFactory、OracleDataBaseFactory 也均需要另外实现 getException 方法

5.3 适用场景

  • 用户只关心产品对应的工厂,无须关心实现细节,甚至无须知道具体产品的类名,这是所有类型的工厂模式都是很重要的
  • 系统中有一个以上的产品族
  • 属于同一个产品族的产品将在一起使用,它们之间可以没有任何关系,但是它们都有共同的约束,例如操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但是它们都是属于某一个操作系统下的,此时具有一个共同的约束条件,即操作系统的类型
  • 产品等级结构稳定

五、建造者模式

设计一个游戏角色,它包含性别、脸型等多个部分组成,不同角色的性别、脸型、服装、发型等外部特性都有差异,例如“天使”拥有美丽的面容和披肩的长发;而“恶魔”极其丑陋,留着光头并穿一件刺眼的黑衣

1. 创建什么

1.1 Actor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Actor {

private String type;

private String sex;

private String face;

private String costume;

private String hairstyle;

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public String getFace() {
return face;
}

public void setFace(String face) {
this.face = face;
}

public String getCostume() {
return costume;
}

public void setCostume(String costume) {
this.costume = costume;
}

public String getHairstyle() {
return hairstyle;
}

public void setHairstyle(String hairstyle) {
this.hairstyle = hairstyle;
}
}

2. 由谁创建

2.1 ActorBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public abstract class ActorBuilder {

protected Actor actor = new Actor();

public abstract void buildType();

public abstract void buildSex();

public abstract void buildFace();

public abstract void buildCostume();

public abstract void buildHairstyle();

/**
* 是否是光头?<br>
* 钩子方法,控制是否执行{@link #buildHairstyle()}
*
* @return 默认为 false
*/
public boolean isBareHeaded() {
return false;
}

public Actor buildActor() {
return actor;
}
}

2.2 AngelBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class AngelBuilder extends ActorBuilder {

@Override
public void buildType() {
actor.setType("天使");
}

@Override
public void buildSex() {
actor.setSex("女");
}

@Override
public void buildFace() {
actor.setFace("漂亮脸蛋");
}

@Override
public void buildCostume() {
actor.setCostume("白裙");
}

@Override
public void buildHairstyle() {
actor.setHairstyle("披肩长发");
}
}

2.3 DevilBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class DevilBuilder extends ActorBuilder {

@Override
public void buildType() {
actor.setType("恶魔");
}

@Override
public void buildSex() {
actor.setSex("男");
}

@Override
public void buildFace() {
actor.setFace("丑陋脸蛋");
}

@Override
public void buildCostume() {
actor.setCostume("刺眼黑衣");
}

@Override
public void buildHairstyle() {
actor.setHairstyle("光头");
}

@Override
public boolean isBareHeaded() {
return true;
}
}

3. 何时创建

3.1 ActorDirector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ActorDirector {
public Actor construct(ActorBuilder ab) {
Actor actor;
ab.buildType();
ab.buildSex();
ab.buildFace();
ab.buildCostume();
if (!ab.isBareHeaded()) {
ab.buildHairstyle();
}
actor = ab.buildActor();
return actor;
}
}

4. 客户端代码

1
2
3
4
Actor actor = new Actor();                                       
ActorDirector actorDirector = new ActorDirector();
ActorBuilder angelBuilder = new AngelBuilder(); // 可引入配置文件和反射机制实现
actor = actorDirector.construct(angelBuilder);

5. 优缺点及适用场景

5.1 优点

  • 耦合性低。客户端不必知道产品内部的组成细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
  • 扩展方便。每一个具体建造者相对独立,与其他建造者无关。
  • 可以精细地控制产品地创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也方便程序进行控制

5.2 缺点

  • 有一定的局限性。适用建造者模式通常创建的产品有较多的共同点,其组成部分相似,如果产品间差异性很大,例如很多组成部分都不同,那么就不适合适用
  • 增加理解难度和运行成本。如果产品的内部变化复杂,困难会导致需要定义很多具体建造者类来实现,这样会使系统变得庞大

5.3 适用场景

  • 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量
  • 需要生成的产品对象的属性相互依赖,需要指定其生成的顺序
  • 对象的创建过程,独立于创建该对象的内在建造者模式中,通过引入指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品

六、原型模式

请为某销售管理系统设计并实现一个客户类Customer,在客户类中包含一个名为客户地址的成员变量,客户地址的类型为Address,用浅克隆和深克隆分别实现Customer对象的复制

深克隆和浅克隆

浅克隆:

  • 基于同一个类型成员变量的地址
  • 复制原型对象
  • 复制基本类型的值

深克隆:

  • 复制同一个类型成员变量的地址
  • 复制原型对象
  • 复制基本类型的值

1. 创建什么

1.1 Address

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Address implements Serializable {

private String name;

public Address(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

2. 由谁创建

2.1 Customer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class Customer implements Serializable, Cloneable {

private Address address;

private String name;

private int age;

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Customer deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Customer) ois.readObject();
}

@Override
public Customer clone() {
Object object;
try {
object = super.clone();
return (Customer) object;
} catch (CloneNotSupportedException e) {
System.out.println("不支持负责");
return null;
}
}
}

3. 何时创建

Customer 的深克隆方法 deepClone 和浅克隆方法 clone

4. 客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void shallow(){
System.out.println("浅克隆:");
Customer customerPrototype = new Customer();
customerPrototype.setAddress(new Address("北京"));
Customer customerClone = customerPrototype.clone();
// 对象比较
System.out.println(customerPrototype == customerClone);
System.out.println(customerPrototype.equals(customerClone));
// 属性类比较
System.out.println(customerPrototype.getAddress() == customerClone.getAddress());
System.out.println(customerPrototype.getAddress().equals(customerClone.getAddress()));
}

public static void deep() throws IOException, ClassNotFoundException {
System.out.println("深克隆:");
Customer customerPrototype = new Customer();
customerPrototype.setAddress(new Address("南京"));
Customer customerClone = customerPrototype.deepClone();
// 对象比较
System.out.println(customerPrototype == customerClone);
System.out.println(customerPrototype.equals(customerClone));
// 属性类比较
System.out.println(customerPrototype.getAddress() == customerClone.getAddress());
System.out.println(customerPrototype.getAddress().equals(customerClone.getAddress()));
}

5. 优缺点及适用场景

5.1 优点

  • 提高效率。使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法, 它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。

5.2 缺点

  • 违背了开闭原则。需要为每个需要克隆的类提供clone方法
  • 可能会提高复杂度。当克隆的代码比较复杂,存在多重的嵌套引用,如果要实现深克隆就需要为每一层对象都支持深克隆

5.3 适用场景

  • 创建新对象成本较大
  • 系统要保存对象的状态,而对象的状态变化很小
  • 复杂度较低,不存在多重的嵌套引用

七、单例模式

1. 创建什么

自己

2. 由谁创建

自己,下列提供三种单例模式

2.1 EagerSingleton

1
2
3
4
5
6
7
8
9
10
public class EagerSingleton {

private static final EagerSingleton instance = new EagerSingleton();

private EagerSingleton() {};

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

2.2 LazySingleton

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LazySingleton {

private volatile static LazySingleton instance = null;

private LazySingleton() {};

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

2.3 IoDHSingleton

1
2
3
4
5
6
7
8
9
10
11
12
public class IoDHSingleton { // Initialization on Demand Holder

private IoDHSingleton() {}

private static class HolderClass {
private final static IoDHSingleton instance = new IoDHSingleton();
}

public static IoDHSingleton getInstance() {
return HolderClass.instance;
}
}

3. 何时创建

自己

4. 客户端代码

1
IoDHSingleton instance = IoDHSingleton.getInstance();

5. 优缺点及适用场景

5.1 优点

  • 提供了唯一实例的受控访问
  • 节约资源。因为系统中只存在一个这样的实例

5.2 缺点

  • 扩展困难。没有抽象层
  • 职责过重。因为单例类可能既提供了业务方法,也需要提供创建自己的方法

5.3 适用场景

  • 系统中只需要一个实例对象
  • 客户调用类的单个实例只允许使用一个公共访问点

创建型模式
https://huajframe.github.io/2022/06/25/设计模式/创建型模式/
作者
HuaJFrame
发布于
2022年6月25日
许可协议