设计模式-创建型-建造者模式

应用场景

如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和易用性,我们可以通过构造函数配合 set() 方法来解决。但是,如果存在下面情况中的任意一种,我们就要考虑使用建造者模式了。

  1. 我们把类的必填属性放到构造函数中,强制创建对象的时候就设置。如果必填的属性有很多,把这些必填属性都放到构造函数中设置,那构造函数就又会出现参数列表很长的问题。如果我们把必填属性通过 set() 方法设置,那校验这些必填属性是否已经填写的逻辑就无处安放了。

  2. 如果类的属性之间有一定的依赖关系或者约束条件,我们继续使用构造函数配合 set() 方法的设计思路,那这些依赖关系或约束条件的校验逻辑就无处安放了。

  3. 如果我们希望创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值,要实现这个功能,我们就不能在类中暴露 set() 方法。构造函数配合 set() 方法来设置属性值的方式就不适用了。

代码实现

实现如下:

和工厂模式的区别

工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。

网上有一个经典的例子很好地解释了两者的区别。

顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉。

对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨。

实战示例

示例:

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
class RedisClientBuilder:
"""
redis client builder

使用方法:
client = RedisClientBuilder.Builder().set_host("127.0.0.1").builder()
client.connection()
client.set_data("test", "test")
"""

host = None
port = None
max_connections = None
password = None
db = None

def __init__(self, builder):
self.host = builder.host
self.port = builder.port
self.max_connections = builder.max_connections
self.password = builder.password
self.db = builder.db
self._connection = None

def connection(self):
if not self._connection:
self._connection = redis.StrictRedis(
connection_pool=redis.ConnectionPool(
host=self.host,
port=self.port,
max_connections=self.max_connections,
db=self.db,
password=self.password,
)
)
return self._connection

def set_data(self, key, value, ex=None, nx=False):
return self._connection.set(key, value, ex, None, nx)

def get_data(self, key):
return self._connection.get(key)

def del_data(self, key):
return self._connection.delete(key)

def zrange_by_score_data(self, key, min_value, max_value, offset=None, count=None):
return self._connection.zrangebyscore(key, min_value, max_value, offset, count)

def flush_db(self):
self._connection.flushdb()

def hgetall(self, name):
hget_result = dict(self._connection.hgetall(name))
# 处理取出的二进制数据并且转成str重新赋值
return_result = dict()
for k in hget_result:
key = bytes.decode(k)
value = bytes.decode(hget_result.get(k))
return_result[key] = value
return return_result

def hset(self, name, key, value):
self._connection.hset(name, key, value)

def hmset(self, key, value, ex=0):
self._connection.hmset(key, value)
if ex > 0:
self._connection.expire(key, ex)

def get_connection(self):
return self._connection

def set_json(self, key, json_value, ex):
json_str = json.dumps(json_value)
return self._connection.set(key, json_str, ex)

def get_json(self, key):
json_str = self._connection.get(key)
if json_str is None:
return None
json_value = json.loads(json_str)
return json_value

def non_blocking_lock(self, name="redis non blocking lock", timeout=1):
"""
非阻塞式分布式锁
:param name: 锁名称
:param timeout: 释放锁时间
:return:
"""
return Lock(self._connection, name=name, blocking=False, timeout=timeout)

class Builder:
DEFAULT_PORT, DEFAULT_MAX_CONNECTIONS, DEFAULT_DB = 6379, 2000, 1

host = None
port = DEFAULT_PORT
max_connections = DEFAULT_MAX_CONNECTIONS
password = None
db = DEFAULT_DB

def set_host(self, value):
if not value:
raise ValueError("host can not be empty")
self.host = value
return self

def set_port(self, value):
self.port = value
return self

def set_max_connections(self, value):
if not isinstance(value, int):
raise ValueError("max_connections must be int")
self.max_connections = value
return self

def set_password(self, value):
self.password = value
return self

def set_db(self, value):
self.db = value
return self

def builder(self):
if not self.host or not self.port or not self.db:
raise ValueError("host, port, db can not be empty")
return RedisClientBuilder(self)