简明 js 设计模式 —— 策略模式

场景

假如要写一个文件解析器。针对不同的文件类型调用不同的方法。如果用一个函数来表达,可能会写出如下代码:

1
2
3
4
5
function fileParser(fileType) {
if (fileType === 'js') jsParser();
if (fileType === 'txt') txtParser();
// ....
}

每增加一种类型,我们需要修改我们的 fileParser 函数,增加一个条件判断。这样不够优雅。

实现

nodejs 的 require 函数也会遇到上面的情况,它需要针对三种不同的类型 —— js、json、node 分别做处理。我们来看看它是如何实现的:

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
// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);
};


// Native extension for .json
Module._extensions['.json'] = function(module, filename) {
const content = fs.readFileSync(filename, 'utf8');

if (manifest) {
const moduleURL = pathToFileURL(filename);
manifest.assertIntegrity(moduleURL, content);
}

try {
module.exports = JSON.parse(stripBOM(content));
} catch (err) {
err.message = filename + ': ' + err.message;
throw err;
}
};


// Native extension for .node
Module._extensions['.node'] = function(module, filename) {
if (manifest) {
const content = fs.readFileSync(filename);
const moduleURL = pathToFileURL(filename);
manifest.assertIntegrity(moduleURL, content);
}
// Be aware this doesn't use `content`
return process.dlopen(module, path.toNamespacedPath(filename));
};

这些函数在 Module.prototype.load 过程中调用。

1
2
3
4
5
Module.prototype.load = function(filename) {

Module._extensions[extension](this, filename);
// ...
}

我们看到这个过程没有了条件判断的流程。而且对多类型支持也非常方便,只需要扩展类型方法即可,不需要修改所谓的 fileParser 方法(符合 open / close 原则)。

策略模式便是在运行时选择算法,并且消除了判断流程的一种模式。

参考

ISSUE

有问题?来 Github 一起讨论。