- Welcome to the Node.js Platform
- Node.js Essential Patterns
- Asynchronous Control Flow Patterns with Callbacks
- Asynchronous Control Flow Patterns with ES2015 and Beyond
- Coding with Streams
- Design Patterns
- Writing Modules
- Advanced Asynchronous Recipes
- Scalability and Architectural Patterns
- Messaging and Integration Patterns
- Welcome to the Node.js Platform
- Node.js 的发展
- Node.js 的特点
- 介绍 Node.js 6 和 ES2015 的新语法
- reactor 模式
- Node.js Essential Patterns
- Asynchronous Control Flow Patterns with Callbacks
- Asynchronous Control Flow Patterns with ES2015 and Beyond
- Coding with Streams
- Design Patterns
- Writing Modules
- Advanced Asynchronous Recipes
- Scalability and Architectural Patterns
- Messaging and Integration Patterns
模板模式( Template )
我们将要分析的下一个模式叫做模板模式,它与策略模式有许多共同点。模板由定义一个抽象的伪类组成,它代表了算法的框架,其中一些步骤是未定义的。然后子类可以通过实现缺少的步骤填充算法中的空白,称为模板方法。这种模式的目的是使定义一个类的家族成为可能,这些类都是类似算法的变体。下面的 UML
图显示了我们刚刚描述的结构:
上图中显示的三个具体类扩展了 Template
并为 templateMethod()
提供了一个实现,使用 C++
术语来说,该实现方法是抽象或者说是虚函数;在 JavaScript
中,这意味着该方法是未定义的或被分配给一个总是抛出异常的函数,这表明该方法必须被实现。模板模式可以被认为比我们目前所看到的其他模式更加符合面向对象思想,因为继承是其实现的核心部分。
模板模式和策略模式的目的非常相似,但两者的主要区别在于它们的结构和实现。两者都允许我们改变算法的某些部分,同时重用公共部分;然而,尽管策略模式允许我们在运行时动态地执行它,但使用模板模式完成算法是在具体类被定义的时候确定的。在这些假设下,模板模式可能更适合那些我们想要创建一个算法的预先打包的变体的情况。与往常一样,一种模式与另一种模式的选择取决于开发者,他们必须考虑每个用例的各种利弊。
使用模板模式的配置管理器
为了更好地了解模板模式和状态模式之间的区别,现在让我们重新实现我们在关于策略模式的章节中定义的 Config
对象,但是这次使用模板模式。就像以前版本的 Config
对象一样,我们希望能够使用不同的文件格式来加载和保存一组配置属性。
首先定义模板类,我们将其称为 ConfigTemplate
:
const fs = require('fs');
const objectPath = require('object-path');
class ConfigTemplate {
read(file) {
console.log(`Deserializing from ${file}`);
this.data = this._deserialize(fs.readFileSync(file, 'utf-8'));
}
save(file) {
console.log(`Serializing to ${file}`);
fs.writeFileSync(file, this._serialize(this.data));
}
get(path) {
return objectPath.get(this.data, path);
}
set(path, value) {
return objectPath.set(this.data, path, value);
}
_serialize() {
throw new Error('_serialize() must be implemented');
}
_deserialize() {
throw new Error('_deserialize() must be implemented');
}
}
module.exports = ConfigTemplate;
新的 ConfigTemplate
类定义了两个模板方法: _deserialize()
和 _serialize()
,它们是执行加载和保存配置所需的。 名称开头的下划线表示它们仅供内部使用,这是一种标记受保护方法的简单方法。由于在 JavaScript
中我们不能将方法声明为抽象方法,我们简单地将它们定义为存根,如果它们被调用(即,如果它们没有被具体子类覆盖)则抛出异常。
现在让我们使用我们的模板创建一个具体的类,例如,允许我们使用 JSON
格式加载和保存配置:
const util = require('util');
const ConfigTemplate = require('./configTemplate');
class JsonConfig extends ConfigTemplate {
_deserialize(data) {
return JSON.parse(data);
};
_serialize(data) {
return JSON.stringify(data, null, ' ');
}
}
module.exports = JsonConfig;
JsonConfig
类从我们的模板, ConfigTemplate
类和 为 _deserialize()
和 _serialize()
方法提供了一个具体的实现。 JsonConfig
类现在可以作为独立的配置对象使用,而不使用 需要指定一个序列化和反序列化的策略,因为它是在类本身中实现的:
const JsonConfig = require('./jsonConfig');
const jsonConfig = new JsonConfig();
jsonConfig.read('samples/conf.json');
jsonConfig.set('nodejs', 'design patterns');
jsonConfig.save('samples/conf_mod.json');
通过使用模板模式,我们可以通过重复使用从父模板类继承的逻辑和接口,仅提供一些抽象方法的实现,从而使我们能够获得一个全新的完全配置管理器。
实际应用场景
这种模式不应该听起来对我们来说是全新的。我们已经在 Chapter 5-Coding with Streams
时遇到过它,当我们扩展不同的 Streams
类来实现我们的自定义流。在这种情况下,模板方法是 _write()
, _read()
, _transform()
或 _flush()
方法,具体取决于我们想要实现的流类。要创建一个新的自定义流,我们需要从一个特定的抽象流类继承,为模板方法提供一个实现。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论