Template 模板模式

设计模式 2020-04-25 2551 字 2505 浏览 点赞

起步

模板模式全称应该是模板方法模式(Template method pattern),属于行为型。其利用继承关系,实现代码复用,同时确保子类扩展能力。

在这里我用 “子类扩展能力” 属于一家之言,主要想表达,模板模式在一定程度上限制了子类的扩展方法,或者说要求子类在预设的扩展点上进行功能扩展。

假设场景

模板方法模式要求在模板方法中定义程序框架或算法,并允许子类重写或自定义算法中的某些步骤,使得算法里的部分操作延迟到子类中实现。

用文字阐述或许让人懵逼,但是一看代码就“懂了”,事实上我们常常在代码里使用。

现在假设项目中需要增加一个解析配置的功能,核心的解析代码交由 Parser 完成,我们是上层调用者,负责提供配置文件的内容,将内容交给 Parser,返回解析好的配置信息。代码呈现如下:

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

    def load(self, file):
        # 1. 获取配置内容
        f = open(file)
        content = f.read()
        # 2. 解析配置
        config = self.parser.parse(content)
        # 3. 释放资源
        f.close()
        return config

Good! But ... 试想一下,要是配置文件不在本地呢?比方说配置信息可能是写在 redis 里的,或者是通过 http 协议提供的,ConfigSource.load 明显不适用。

模板模式

首先我们需要一个模板方法,这个方法提供具体的操作步骤。步骤可以粗略分三个阶段:1. 配置内容的来源,2. 解析配置,3. 配置信息的去向。

class ConfigSource(metaclass=abc.ABCMeta):
    def __init__(self, parser):
        self.parser = parser

    @abc.abstractmethod
    def read_config(self):
        pass

    @abc.abstractmethod
    def send_config(self, config):
        pass

    def load(self, file):
        # 1. 来源
        content = self.read_config()
        # 2. 解析配置
        config = self.parser.parse(content)
        # 3. 去向
        return self.send_config(config)

负责控制流程的 ConfigSource.load 方法我们称之为 “模板方法”,这就是模板方法模式称呼的来源。

假设配置文件就在本地,解析出来的配置信息放在内存中,则代码可以设计如下:

class LocalConfigSource(ConfigSource):
    def read_config(self):
        # 读取本地文件
        with open("config_file_path", "r") as f:
            return f.read()

    def send_config(self, config):
        # 直接返回
        return config

而如果配置文件在远端,我们需要借助 tcp 协议获取远端的配置文件,完成解析后再发往远端。

class RemoteConfigSource(ConfigSource):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        ...  # tcp sock、bind、listen、accept 等操作
        self.conn = ...

    def read_config(self):
        config = self.conn.read(1024 * 10)
        return config.decode("utf-8")

    def send_config(self, config):
        byte_arry = config.encode("utf-8")
        self.conn.send(byte_arry, len(config))
        self.conn.close()

总结

模板模式是常见而简单的设计模式,对使用者的“抽象”能力要求很高。一旦抽象得不够好,很可能造成子类重写模板方法,就得不偿失了。

在我看来,模板模式很有些控制反转的味道,在各大框架中频繁出现,值得重视。

参考



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

还不快抢沙发

添加新评论