Builder 建造者模式

设计模式 2020-04-30 3263 字 914 浏览 点赞

起步

命令模式属于创建型,旨在解决多参数初始化类带来的复杂设计问题。

“多参数初始化类”的意思就是,实例化一个对象时需要传入大量的变量。另一方面,也许这些传入的变量之间存在一定的关系约束,比如需要构建一个正方形对象,那就要求传入的长宽值相等。

类的设计问题

现在我们需要设计一个读取配置的类,要求指定配置文件的路径。这个需求很简单,代码很好实现:

class ConfigSource(object):
    def __init__(self, path):
        self._path = path  # 文件路径

    def load(self):
        """ 加载配置 """
        ...

后来需求增加,譬如要求判断配置文件的时效性,如果不在时效期内,不读文件;要求判断客户端的使用权限,如果是普通用户,部分配置内容不可读(不做解析)。对应的设计就是:

class ConfigSource(object):
    def __init__(self, path, time_range, ower):
        self._path = path  # 文件路径
        self._time_range = time_range
        self._ower = ower

    def load(self):
        ...

上述代码看上去还好,然后类似的需求继续累加很可能出现这样一种场景:

class ConfigSource(object):
    def __init__(self, path, time_range, ower, a, b, c, d, e, f):
        self._path = path  # 文件路径
        self._time_range = time_range
        self._ower = ower
        self._a = a
        self._b = b
        ...

    def load(self):
        ...

# 使用
cs = ConfigSource(path, time_range, ower, a, b, c, ...)

就是需要传入的初始化参数越来越多,实例化对象越来越繁琐,代码不好看了。

有什么法子可以减少实例化参数呢?我们完全可以用 set_xxx() 方法来解决。譬如:

class ConfigSource(object):
    def __init__(self, path):
        self._path = path

    def set_time_range(self, time_range):
        self._time_range = time_range

    def set_ower(self, ower):
        self._ower = ower

    ...
    
    def load(self):
        ...

实例化参数被有效减少,可另一个问题也来了。现在要求 time_range,ower 同时被传入。但是客户端通过 set_xxx() 函数设置参数,存在只调用 set_time_range 而没有调用 set_ower 的情况,继而 load 方法的执行逻辑也会受影响。

既然初始化参数那么复杂,根据复杂就要分层的设计原则,我们应该把生成初始化参数这事交给别人来做。这个别人不是他人,就是马上要说的建造者

建造者模式

建造者模式要求把复杂类的构造器独立出来,在一定程度上降低了原来类的复杂度。上述代码可修改为:

class ConfigSourceBuilder(object):
    def set_path(self, path):
        if not os.path.exists(path):
            raise TypeError()
        self.path = path
        return self

    def set_time_range(self, time_range):
        if not time_range:
            raise TypeError()
        self.time_range = time_range
        return self

    def set_ower(self, ower):
        if not ower:
            raise TypeError()
        self.ower = ower
        return self

    ...

    def build(self):
        """ 构造初始化参数对象(参数校验职责) """
        if not all([self.path, self.time_range, self.ower]):
            raise TypeError()
        return self

class ConfigSource(object):
    def __init__(self, builder):
        self._path = builder.path  # 文件路径
        self._time_range = builder.time_range  # 失效范围
        self._ower = builder.ower  # 所有者

    def load(self):
        ...

对应的客户端用法:

builder = ( 
    ConfigSourceBuilder()
    .set_path(...)
    .set_time_range(...)
    .set_ower(...)
    .build()
)

cs = ConfigSource(builder)

总结

事实上建造者模式在代码实现细节上可以更多样化,只要记住核心思想:隔离类与类的构造器。甚至你可以片面觉得 xxxBuilder 就是在行使参数校验的功能,而让原有类更多关注自己的功能逻辑。

建造者模式降低了原来类的复杂度,同时也增加了重复代码——大量相同的属性名将出现在两个类中。这也使得 ConfigSource 与 ConfigSourceBuilder 之间的耦合度很高。当然,你可以通过接口解耦,这就属于建造者模式的其他表现形式了。

参考



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论