0%

5种创建型设计模式

创建型设计模式:关注对象的实例化过程。

创建型设计模式共5种:工厂方法设计模式,抽象工厂设计模式,构建者设计模式,原型设计模式和单例设计模式。
助记:

  • 抽象工厂、工厂方法
  • 原型、单例
  • 构建者

一、工厂方法设计模式

英文名:Factory Method Pattern。

1.1、痛点

不使用new等直接方法创建目标类的实例对象,而是使用工厂方法创建,针对痛点:

  1. 便于使用
  2. 便于扩展

1.2、实现

有两种实现形式:

  • 实例工厂方法
  • 静态工厂方法

1.2.1、实例工厂方法实现形式

分“目标类只有1个”和“目标类不只1个,且具有共同父类”两种情况进行介绍。

1.2.1.1、目标类只有1个

示例代码如下:

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

int age;
}

class UserFactory {

User createUser() {
User user = new User();
user.name = "dslztx";
user.age = 35;

return user;
}
}

public class FactoryMethodExample {
public static void main(String[] args) {
UserFactory userFactory = new UserFactory();
User user = userFactory.createUser();
}
}
1.2.1.2、目标类不只1个,且具有共同父类

讲解设计模式的教材/材料在介绍工厂方法设计模式时常基于该场景。

示例代码如下[1]:

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
54
// 首先,我们需要定义一个图形接口
interface Shape {
void draw();
}

// 然后,我们实现两个具体的图形类,分别是 Circle(圆形)和 Rectangle(矩形)
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}

class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}

// 接下来,我们创建一个抽象工厂类 ShapeFactory
// 它定义了一个抽象的工厂方法 createShape,子类将实现这个方法来创建具体的图形对象
abstract class ShapeFactory {
abstract Shape createShape();
}

// 然后,我们创建两个具体的工厂类,分别是 CircleFactory 和 RectangleFactory
// 它们分别实现了 ShapeFactory 并重写了 createShape 方法来返回相应的图形对象
class CircleFactory extends ShapeFactory {
@Override
Shape createShape() {
return new Circle();
}
}

class RectangleFactory extends ShapeFactory {
@Override
Shape createShape() {
return new Rectangle();
}
}

// 我们可以使用这些工厂类来创建图形对象
public class FactoryMethodExample {
public static void main(String[] args) {
ShapeFactory circleFactory = new CircleFactory();
Shape circle = circleFactory.createShape();
circle.draw();

ShapeFactory rectangleFactory = new RectangleFactory();
Shape rectangle = rectangleFactory.createShape();
rectangle.draw();
}
}

1.2.2、静态工厂方法实现形式

分“目标类只有1个”和“目标类不只1个,且具有共同父类”两种情况进行介绍。

1.2.2.1、目标类只有1个

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class User {
String name;

int age;
}

class UserFactoryStatic {
static User createUser() {
User user = new User();
user.name = "dslztx";
user.age = 35;

return user;
}
}

public class FactoryMethodExample {
public static void main(String[] args) {
User user = UserFactoryStatic.createUser();
}
}
1.2.2.2、目标类不只1个,且具有共同父类

示例代码如下:

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
// 首先,我们需要定义一个图形接口
interface Shape {
void draw();
}

// 然后,我们实现两个具体的图形类,分别是 Circle(圆形)和 Rectangle(矩形)
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}

class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}

class ShapeFactoryStatic {
// 利用类型参数来创建具体图形子类对象
static <T extends Shape> T createShape(Class<T> tClass) {
if (tClass == Circle.class) {
return (T) new Circle();
} else if (tClass == Rectangle.class) {
return (T) new Rectangle();
}
return null;
}
}

public class FactoryMethodExample {
public static void main(String[] args) {
Shape circle = ShapeFactoryStatic.createShape(Circle.class);
circle.draw();

Shape rectangle = ShapeFactoryStatic.createShape(Rectangle.class);
rectangle.draw();
}
}

1.3、JDK中的例子

JDK中的例子:

  1. Integer.valueOf(int i):方便扩展成缓存[-128,127]值范围内的Integer对象进行复用
  2. Object.toString()
  3. Calendar getInstance()

二、抽象工厂设计模式

英文名:Abstract Factory Pattern。

另外一个名字为:Kit Pattern,工具箱设计模式。

2.1、痛点

实现需求:需要创建一系列相关或相互依赖的对象,这些对象属于一组相关的产品族,同时,系统需要保证这些产品族之间的一致性。

2.2、实现

实现必存在工厂类父类,且该工厂类父类中必存在抽象工厂方法。

注意与“工厂方法设计模式”实现的区分:对于实现必存在工厂类父类,且该工厂类父类中必存在抽象工厂方法,其属于“工厂方法设计模式”中的实例工厂方法实现形式;反之,“工厂方法设计模式”中还有静态工厂方法实现形式,也有非实现必存在工厂类父类,且该工厂类父类中必存在抽象工厂方法实例工厂方法实现形式。故“抽象工厂设计模式”属于“工厂方法设计模式”的子集

示例代码如下[1]:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// 抽象产品接口:操作系统
interface OperatingSystem {
void run();
}

// 抽象产品接口:应用程序
interface Application {
void open();
}

// 抽象工厂接口
interface SoftwareFactory {
OperatingSystem createOperatingSystem();

Application createApplication();
}

// 具体产品:Windows操作系统
class WindowsOS implements OperatingSystem {
@Override
public void run() {
System.out.println("Running Windows OS");
}
}

// 具体产品:Linux操作系统
class LinuxOS implements OperatingSystem {
@Override
public void run() {
System.out.println("Running Linux OS");
}
}

// 具体产品:Word应用程序
class WordApplication implements Application {
@Override
public void open() {
System.out.println("Opening Word Application");
}
}

// 具体产品:Excel应用程序
class ExcelApplication implements Application {
@Override
public void open() {
System.out.println("Opening Excel Application");
}
}

// 具体工厂:Windows工厂
class WindowsFactory implements SoftwareFactory {
@Override
public OperatingSystem createOperatingSystem() {
return new WindowsOS();
}

@Override
public Application createApplication() {
return new ExcelApplication();
}
}

// 具体工厂:Linux工厂
class LinuxFactory implements SoftwareFactory {
@Override
public OperatingSystem createOperatingSystem() {
return new LinuxOS();
}

@Override
public Application createApplication() {
return new WordApplication();
}
}

// 在这个示例中,抽象工厂模式通过SoftwareFactory接口和其实现类来创建不同类型的操作系统和应用程序。
// 客户端代码可以根据需要选择不同的工厂实例来创建不同的产品组合。
public class Client {
public static void main(String[] args) {
SoftwareFactory windowsFactory = new WindowsFactory();
OperatingSystem windowsOS = windowsFactory.createOperatingSystem();
Application windowsApp = windowsFactory.createApplication();

windowsOS.run();
windowsApp.open();

SoftwareFactory linuxFactory = new LinuxFactory();
OperatingSystem linuxOS = linuxFactory.createOperatingSystem();
Application linuxApp = linuxFactory.createApplication();

linuxOS.run();
linuxApp.open();
}
}

2.3、JDK中的例子

2.3.1、java.awt.Toolkit

工厂类父类java.awt.Toolkit和其中3个抽象工厂方法(实际不只3个,这里只罗列3个进行举例说明):

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

protected abstract ButtonPeer createButton(Button target) throws HeadlessException;

protected abstract TextFieldPeer createTextField(TextField target) throws HeadlessException;

protected abstract LabelPeer createLabel(Label target) throws HeadlessException;

}

Button产品的父类:

1
2
public interface ButtonPeer extends ComponentPeer {
}

TextField产品的父类:

1
2
public interface TextFieldPeer extends TextComponentPeer {
}

Label产品的父类:

1
2
public interface LabelPeer extends ComponentPeer {
}
2.3.1.1、Windows JDK

java.awt.Toolkit的具体子类sun.awt.windows.WToolkit

1
2
public final class WToolkit extends SunToolkit implements Runnable {
}

Button产品的具体子类sun.awt.windows.WButtonPeer

1
2
final class WButtonPeer extends WComponentPeer implements ButtonPeer {
}

TextField产品的具体子类sun.awt.windows.WTextFieldPeer

1
2
final class WTextFieldPeer extends WTextComponentPeer implements TextFieldPeer {
}

Label产品的具体子类sun.awt.windows.WLabelPeer

1
2
final class WLabelPeer extends WComponentPeer implements LabelPeer {
}
2.3.1.2、Linux JDK

java.awt.Toolkit的具体子类sun.awt.X11.XToolkit

1
2
public final class XToolkit extends UNIXToolkit implements Runnable {
}

Button产品的具体子类sun.awt.X11.XButtonPeer

1
2
public class XButtonPeer extends XComponentPeer implements ButtonPeer {
}

TextField产品的具体子类sun.awt.X11.XTextFieldPeer

1
2
final class XTextFieldPeer extends XComponentPeer implements TextFieldPeer {
}

Label产品的具体子类sun.awt.X11.XLabelPeer

1
2
class XLabelPeer extends XComponentPeer implements LabelPeer {
}

三、构建者设计模式

英文名:Builder Pattern。

3.1、痛点

适用场景:对于一个实例对象,很难一次性构建完成或者一次性构建完成代价很大。它的本质是将构建实例对象所需的信息从“一次性输入”改为“非一次性输入”。

3.2、实现

有两种实现形式:

  • 二角色——Client和Builder,实际经常使用的实现形式
  • 三角色——Client,Director和Builder,很多设计模式书籍建议的实现形式,引入Director的本意是将Client和Builder解耦,但也引入了额外的维护成本,故权衡下来,在引入构建者设计模式时经常使用“二角色”实现形式

备注:多见“Builder实例的构建方法返回Builder实例本身”以进行链式调用

3.2.1、“二角色”实现形式

包含Client和Builder两种角色,Builder角色负责构建实例对象,Client角色(即业务代码)直接与Builder角色进行交互。

示例代码如下:

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
54
55
56
57
58
59
60
61
62
63
64
65
class House {

String foundation;
String structure;
String roof;
String interior;

}

class HouseBuilder {
private House house = null;

public HouseBuilder() {
house = new House();
}

public void buildFoundation(String foundation) {
house.foundation = foundation;
}

public void buildStructure(String structure) {
house.structure = structure;
}

public void buildRoof(String roof) {
house.roof = roof;
}

public void buildInterior(String interior) {
house.interior = interior;
}

public House getHouse() {
return house;
}
}

public class Client {
public static void main(String[] args) {

HouseBuilder houseBuilder = new HouseBuilder();

//刚开始获取不到,经过很复杂的计算才获得foundation变量值
String foundation = "FakeFoundation";
houseBuilder.buildFoundation(foundation);

//刚开始获取不到,经过很复杂的计算才获得structure变量值
String structure = "FakeStructure";
houseBuilder.buildStructure(structure);

//刚开始获取不到,经过很复杂的计算才获得roof变量值
String roof = "FakeRoof";
houseBuilder.buildRoof(roof);

//刚开始获取不到,经过很复杂的计算才获得interior变量值
String interior = "FakeInterior";
houseBuilder.buildInterior(interior);

System.out.println(houseBuilder.getHouse());

// 例子所描述的场景中,很难一次性获得构建House实例对象所需的信息,构建者设计模式很好地解决这个问题
// Client直接与Builder进行交互

}
}

3.2.2、“三角色”实现形式

包含Client、Director和Builder三种角色,Builder角色负责构建实例对象,Client角色(即业务代码)通过Director角色与Builder角色进行交互。

示例代码如下:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class House {

String foundation;
String structure;
String roof;
String interior;

}

class HouseBuilder {
private House house = null;

public HouseBuilder() {
house = new House();
}

public void buildFoundation(String foundation) {
house.foundation = foundation;
}

public void buildStructure(String structure) {
house.structure = structure;
}

public void buildRoof(String roof) {
house.roof = roof;
}

public void buildInterior(String interior) {
house.interior = interior;
}

public House getHouse() {
return house;
}
}

class Director {
private HouseBuilder houseBuilder;

public Director(HouseBuilder builder) {
this.houseBuilder = builder;
}

public House constructHouse() {
//刚开始获取不到,经过很复杂的计算才获得foundation变量值
String foundation = "FakeFoundation";
houseBuilder.buildFoundation(foundation);

//刚开始获取不到,经过很复杂的计算才获得structure变量值
String structure = "FakeStructure";
houseBuilder.buildStructure(structure);

//刚开始获取不到,经过很复杂的计算才获得roof变量值
String roof = "FakeRoof";
houseBuilder.buildRoof(roof);

//刚开始获取不到,经过很复杂的计算才获得interior变量值
String interior = "FakeInterior";
houseBuilder.buildInterior(interior);

return houseBuilder.getHouse();
}
}

public class Client {
public static void main(String[] args) {

HouseBuilder concreteBuilder = new HouseBuilder();

Director director = new Director(concreteBuilder);

House concreteHouse = director.constructHouse();

System.out.println("Concrete House: " + concreteHouse);


// 例子所描述的场景中,很难一次性获得构建House实例对象所需的信息,构建者设计模式很好地解决这个问题
// Client通过Director与Builder进行交互

}
}

3.3、JDK中的例子

3.3.1、StringBuilder

构建一个String实例对象不能一次性输入所有信息,须多次输入,适合使用StringBuilder。

采用“二角色”实现形式的示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Client {
public static void main(String[] args) {


StringBuilder sb = new StringBuilder();

sb.append("Subject");

sb.append("Content");

sb.append("Footer");

System.out.println(sb.toString());

}
}

四、原型设计模式

英文名:Prototype Pattern。

4.1、痛点

实现业务需求:根据已存在实例对象克隆“具有一模一样值”的实例对象。

克隆具有克隆深浅的属性,关于克隆深浅有两点说明:

  1. 越浅的克隆,克隆对象与原对象共享内存越多,两者互相独立性越差;越深的克隆,克隆对象与原对象共享内存越少,两者互相独立性越好
  2. 经常简单将克隆分为“浅度克隆”和“深度克隆”,其实是错误的,因为“浅度克隆”和“深度克隆”之间没有明确的界限

在以下示例代码中,呈现了对于shallowClone -> deepClone -> deepMoreClone,克隆越来越深(佐证了难以简单划分“浅度克隆”和“深度克隆”),克隆对象与原对象共享内存越来越少,两者互相独立性越来越好。

示例代码如下:

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
class User {

String name;

int age;

public User shallowClone() {
User user = new User();
user.name = name;
user.age = age;

return user;
}

}

class UserRepo {

List<User> userList = new ArrayList<User>();

public UserRepo shallowClone() {
UserRepo userRepo = new UserRepo();
userRepo.userList = userList;

return userRepo;
}

public UserRepo deepClone() {
UserRepo userRepo = new UserRepo();
userRepo.userList = new ArrayList<>(userList);

return userRepo;
}

public UserRepo deepMoreClone() {
UserRepo userRepo = new UserRepo();

List<User> tmpUserList = new ArrayList<>();
for (User user : userList) {
tmpUserList.add(user.shallowClone());
}

userRepo.userList = tmpUserList;

return userRepo;
}

}

4.2、实现

有3种实现形式

  • 自实现复制方法
  • JDK的Object.clone()原生机制
  • 第三方复制框架

4.2.1、自实现复制方法

示例代码如下:

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
class User {
String name;

int age;
}

public class Client {
public static void main(String[] args) {

User user = new User();
user.name = "dslztx";
user.age = 35;

User copiedUser = copy(user);
System.out.println(copiedUser.name);
System.out.println(copiedUser.age);
}

public static User copy(User a) {
User user = new User();
user.name = a.name;
user.age = a.age;
return user;
}
}

4.2.2、JDK的Object.clone()原生机制

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class User implements Cloneable {
String name;

int age;

@Override
public User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}

public class Client {
public static void main(String[] args) throws CloneNotSupportedException {

User user = new User();
user.name = "dslztx";
user.age = 35;

User copiedUser = user.clone();
System.out.println(copiedUser.name);
System.out.println(copiedUser.age);
}
}

4.2.3、第三方复制框架

使用第三方复制框架,比如“Apache commons-beanutils包中的BeanUtils.copyProperties工具方法”。

4.3、JDK中的例子

4.3.1、ArrayList的clone()方法

重写覆盖Object.clone()方法实现ArrayList自己的clone()方法:

1
2
3
4
5
6
7
8
9
10
11
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}

五、单例设计模式

英文名:Singleton。

5.1、痛点

实现业务需求:全局具有唯一实例。

5.2、实现

单例有多种实现形式,参见《单例》

5.3、JDK中的例子

5.3.1、java.lang.Runtime实例对象

java.lang.Runtime实例对象是单例的。

1
2
3
4
5
6
7
public class Runtime {
private static Runtime currentRuntime = new Runtime();

public static Runtime getRuntime() {
return currentRuntime;
}
}

参考文献

[1]https://mp.weixin.qq.com/s/H16g_yZwRMIKagPb8oNIYQ
[2]https://www.bilibili.com/read/cv18266097

您的支持将鼓励我继续分享!