【设计模式】生成器模式与单例模式

本文最后更新于:2023年12月17日 下午

Builder(生成器)——对象创建型模式

1. 意图

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以器创建不同的表示。

2. 动机

面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成,由于需求变化,这个复杂对象的各个部分经常变化,但它们组合在一起的算法很稳定。

3. 适用性

在以下情况下使用Builder模式:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
  • 当构造过程必须允许被构造的对象有不同的表示时

4. 结构

结构示意图

5. 参与者

  • Builder
    • 为创建一个Product对象的各个部件指定抽象接口
  • ConcreteBuilder
    • 实现Builder的接口以构造和装配该产品的各个部件
    • 定义并跟踪它所创建的表示
    • 提供一个检索产品的接口
  • Director
    • 构造一个使用Builder接口的对象
  • Product
    • 表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程
    • 包含定义组成部件的类,包括将这些部件装配成最终产品的接口

6. 协作

  • 客户创建Director对象,并用它所想要的Builder对象进行配置

  • 一旦生成了产品部件,导向器就会通知生成器

  • 生成器处理导向器的请求,并将部件添加到该产品中

  • 客户从生成器中检索产品

下面的交互图说明了Builder和Director是如何与一个客户协作的。

交互图

7. 效果

  • 它使你可以改变一个产品的内部表示。Builder对象提供给导向器一个构造产品的抽象接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如何装配的。因为产品是通过抽象接口构造的,你在改变产品的内部表示时所要做的只是定义一个新的生成器。
  • 它将构造代码和表示分开。Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。客户不需要知道定义产品内部结构的类的所有信息,这些类是不出现在Builder接口中的。
  • 它使你可对构造过程进行更精细的控制。Builder模式与一下子就生成产品的创建模式不同,它是在导向器的控制下一步一步构造产品的。

8. 示例代码

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
95
96
97
98
99
100
101
102
// Builder.h
// Created by 张宇轩 on 2022/4/17.
// Copyright (c) 2022 zhangyuxuan. All rights reserved.
//
#include <iostream>
#include <string>

#ifndef CREATIONPATTERNDESIGN_BUILDER_H
#define CREATIONPATTERNDESIGN_BUILDER_H

using namespace std;

void test_builder();

// 需要构造的产品,这里就是电脑,简化实现,把其中需要生产的零部件都用字符串表示
struct Computer {
string cpu;
string memory;
string mainboard;
void show_info() {
cout <<"CPU:" << cpu << " memory:" << memory << " mainboard:" << mainboard << endl;
}
};

// Builder类,定义一系列生产零部件的接口
class ComputerBuilder {
public:
virtual void build_computer() {
computer = new Computer;
}

virtual void build_cpu() {}

virtual void build_mainboard() {}

virtual void build_memory() {}

virtual Computer *get_computer() {
if (computer != nullptr) {
return computer;
}
return nullptr;
}

protected:
Computer *computer;
};

// 具体的Builder类
class ComputerBuilderA : public ComputerBuilder {
virtual void build_cpu() {
if (computer != nullptr) {
computer->cpu = "Intel";
}
}
virtual void build_mainboard() {
if (computer != nullptr) {
computer->mainboard = "微星";
}
}
virtual void build_memory() {
if (computer != nullptr) {
computer->memory = "三星";
}
}
};

class ComputerBuilderB : public ComputerBuilder {
virtual void build_cpu() {
if (computer != nullptr) {
computer->cpu = "AMD";
}
}
virtual void build_mainboard() {
if (computer != nullptr) {
computer->mainboard = "华硕";
}
}
virtual void build_memory() {
if (computer != nullptr) {
computer->memory = "致钛";
}
}

};

// Director类,即借助使用 Builder,来控制电脑生产
class Factory {
public :
Factory(ComputerBuilder* builder) : builder(builder) {}
Computer* make_computer() {
builder->build_computer();
builder->build_cpu();
builder->build_mainboard();
builder->build_memory();
return builder->get_computer();
}
private:
ComputerBuilder *builder;

};
#endif //CREATIONPATTERNDESIGN_BUILDER_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Builder.cpp
// Created by 张宇轩 on 2022/4/17.
// Copyright (c) 2022 zhangyuxuan. All rights reserved.
//
#include <iostream>
#include "Builder.h"
void test_builder() {
cout << "——————————开始测试Builder模式——————————" << endl;
ComputerBuilder* cba = new ComputerBuilderA;
ComputerBuilder* cbb = new ComputerBuilderB;
Factory fa(cba);
Factory fb(cbb);
fa.make_computer()->show_info();
fb.make_computer()->show_info();

cout << "——————————结束测试Builder模式——————————" << endl;
}

9. 相关模式

Abstract Factory与Builder相似,因为它也可以创建复杂对象。主要区别是Builder模式着重于一步步构造一个复杂对象。而Abstract Factory 着重于多个系列的产品对象(简单的或复杂的)。Builder在最后一步返回产品,而对于Abstract Factory 来说,产品是立即返回的。

Composite模式通常是用Builder生成的。

Singleton(单例)——对象创建型模式

1. 意图

保证一个类仅有一个实例,并提供一个访问它的全局访问点

2. 动机

对于一些类来说,只有一个实例是很重要的。例如,虽然系统中可以有许多打印机,但却只应该有一个打印假脱机(printer spooler) ,只应该有一个文件系统和一个窗口管理器。一个会计系统只能专用于一个公司。

怎样才能保证一个类只有一个实例并且这个实例易于被访问呢?全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象。

一个更好的办法是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法。这就是Singleton模式。

3. 适用性

在下面的情况下可以使用Singleton模式:

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无序更改代码就能使用一个扩展的实例时。

4. 结构

结构示意图

5. 参与者

  • Singleton
    • 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作(可以是C++中的一个静态成员函数)
    • 可能负责创建它自己的唯一实例

6. 协作

  • 客户只能通过Singleton的Instance操作访问一个Singleton的实例

7. 效果

Singleton模式有许多优点:

  • 对唯一实例的受控访问。因为Singleton类封装它的唯一实例,所以它可以严格地控制客户怎样以及何时访问它。
  • 缩小命名空间。Singleton模式是对全局变量的一种 改进,它避免了那些存储唯一实例的全局变量污染命名空间
  • 允许对操作和表示的精化。Singleton类可以有子类,而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时配置应用
  • 允许可变数目的实例。这个模式使得你易于改变你的想法,并允许Singleton类的多个实例。

8. 实现

下面是使用Singleton模式时需要考虑的实现问题。

  • 保证一个唯一的实例。在C++中可以用Singleton类的静态成员函数Instance来定义这个类操作
  • Instance可以使用惰性(lazy)初始化,即只有第一次被访问时才创建实例
  • 创建Singleton类的子类。最简单的技术是在Singleton的Instance操作中决定你想使用的是哪一个单例。另一个选择Singleton的子类的方法是将Instance的实现从父类中分离出来并将它放入子类。
    • 一个更灵活的方法是使用一个单例注册表(registry of singleton)。可能的Singleton类的集合不是由Instance定义的,Singleton类可以根据名字在一个众所周知的注册表中注册它们的单例实例。这个注册表可以在字符串名字和单例之间建立映射。当Instance需要一个单例时,它参考注册表,根据名字请求单例。

9. 示例代码

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
// Singleton.h
// Created by 张宇轩 on 2022/4/17.
// Copyright (c) 2022 zhangyuxuan. All rights reserved.
//
#include <iostream>

#ifndef CREATIONPATTERNDESIGN_SINGLETON_H
#define CREATIONPATTERNDESIGN_SINGLETON_H

// 最简单的单例实现方式,使用局部静态变量,惰性分配(懒汉),并且在C++11之后能够保证线程安全
class Singleton {
public:
static Singleton* get_instance() {
static Singleton instance;
return &instance;
}
private:
Singleton() {}
~Singleton() {}
Singleton(const Singleton& singleton) = delete;
Singleton operator= (const Singleton& singleton) = delete;
};

// 懒汉,双重检查锁
class Singleton2{
public:
static Singleton2* get_instance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mutex);
if (instance == nullptr) {
instance = new Singleton2();
}
}
return instance;
}

private:
static Singleton2* instance;
static std::mutex mutex;

// 定义一个内部垃圾回收类
class GC {
public:
~GC() {
if(Singleton2::instance != nullptr) {
delete Singleton2::instance;
Singleton2::instance = nullptr;
}
}
};
static GC gc;
};
Singleton2* Singleton2::instance = nullptr;
Singleton2::GC Singleton2::gc;
std::mutex Singleton2::mutex;


// 饿汉
class Singleton3 {
public:
static Singleton3* get_instance() {
return &instance;
}
private:
Singleton3() {}
~Singleton3() {}
Singleton3(const Singleton3& singleton) = delete;
Singleton3 operator= (const Singleton3& singleton) = delete;
static Singleton3 instance;
};
Singleton3 Singleton3::instance;

#endif //CREATIONPATTERNDESIGN_SINGLETON_H

10. 相关模式

很多模式都可以用Singleton模式实现,比如Abstract Factory,Builder,Prototype。


【设计模式】生成器模式与单例模式
https://2017zhangyuxuan.github.io/2022/04/17/2022-04/2022-04-17 【设计模式】生成器模式与单例模式/
作者
Zhang Yuxuan
发布于
2022年4月17日
许可协议