如何在 TypeScript 中使用模板方法模式实现可扩展的文件解析器。
欢迎来到 TypeScript 设计模式系列,该系列介绍了使用 TypeScript 进行 Web 开发的一些有用的设计模式。
之前的文章如下:
设计模式对于 web 开发人员非常重要,掌握它们可以让我们写出更好的代码。在本文中,我将使用 TypeScript 来介绍模板方法模式。
CSV(Comma-Separated Values)是一种通用的、相对简单的文件格式,CSV文件以纯文本的形式存储表格数据(数字和文本),当你需要处理CSV数据时,相应的处理流程如下图所示:
理解了上面的处理流程后,让我们使用 Node.js 来实现解析 csv 文件的功能。
users.csv
id,Name
1,Bytefer
2,Kakuqo
parse-csv.ts
import fs from "fs";
import path from "path";
import * as url from "url";
import { csvParse } from "d3-dsv";
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
const processData = (fileData: any[]) => console.dir(fileData);
const content = fs.readFileSync(path.join(__dirname, "users.csv"), "utf8");
const fileData = csvParse(content);
processData(fileData);
在上面的代码中,我们导入了d3-dsv模块来实现csv的解析函数,然后使用esno来执行parse-cvs.ts文件:
$ npx esno parse-csv.ts
当上述代码运行成功时,终端将输出以下结果:
[
{ id: Ƈ', Name: 'Bytefer' },
{ id: ƈ', Name: 'Kakuqo' },
columns: [ 'id', 'Name' ]
]
Markdown是一种轻量级的标记语言,它允许人们以易读易写的纯文本格式编写文档。为了在网页上显示Markdown文档,我们必须将Markdown文档转换为HTML文档。
为了实现上述功能,我们的处理流程如下:
理解了上面的处理流程之后,让我们使用 Node.js 来实现解析 Markdown 文件的功能。
Users.md
### Users
- Bytefer
- Kakuqo
parse-md.ts
import fs from "fs";
import path from "path";
import * as url from "url";
import { marked } from 'marked'
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
const processData = (fileData: any[]) => console.dir(fileData);
const content = fs.readFileSync(path.join(__dirname, "Users.md"), "utf8");
const fileData = marked.parse(content);
processData(fileData);
在上面的代码中,我们导入了 marked 模块来实现 Markdown 文件的解析功能,然后使用 esno 来执行 parse-md.ts 文件:
$ npx esno parse-md.ts
当上述代码运行成功时,终端将输出以下结果:
'<h3 id="users">Users</h3>\n<ul>\n<li>Bytefer</li>\n<li>Kakuqo</li>\n</ul>\n'
对于前面两个示例,尽管解析了不同类型的文件,但您会发现它们的解析过程是相似的。
整个过程主要包括三个步骤:读取文件、解析文件和处理数据,对于该场景,我们可以引入模板方法模式来封装上述三个步骤的处理顺序。
模板方法模式由两个部分组成:一个抽象父类和一个具体的实现子类。通常,子类的算法框架被封装在抽象父类中,它还包括一些公共方法的实现和封装子类中所有方法的执行顺序。通过继承这个抽象类,子类也继承了整个算法结构,并可以选择重载父类的方法。
接下来,让我们看看如何使用模板方法模式实现 CSV 解析器和 Markdown 解析器。
为了更好地理解下面的代码,让我们先看看相应的 UML 类图:
在上图中,我们定义了一个抽象类 FileParser
,然后分别定义了两个子类 CsvParser
和 MarkdownParser
。
FileParser类
abstract class FileParser {
// Template Method
parse(filePath: string) {
let content = this.readFile(filePath);
let fileData = this.parseFile(content);
this.processData(fileData);
}
readFile(filePath: string) {
if (fs.existsSync(filePath)) {
return fs.readFileSync(filePath, "utf8");
}
}
abstract parseFile(fileContent: string): any;
processData(fileData: any[]) {
console.log(fileData);
}
}
抽象类 FileParser
中的 parse
方法是所谓的模板方法,我们在这个方法中封装了文件处理的过程。
CsvParser类
class CsvParser extends FileParser {
parseFile(fileContent: string) {
return csvParse(fileContent);
}
}
MarkdownParser 类
class MarkdownParser extends FileParser {
parseFile(fileContent: string) {
return marked.parse(fileContent);
}
}
有了 CsvParser
和 MarkdownParser
这两个类,我们可以用以下方式解析 CSV 和 Markdown 文件:
const csvParser = new CsvParser();
csvParser.parse(path.join(__dirname, "Users.csv"));
const mdParser = new MarkdownParser();
mdParser.parse(path.join(__dirname, "Users.md"));
当你成功运行上述代码时,相应的输出如下图所示:
使用模板方法模式,我们重新实现了对 CSV 和 Markdown 文件的解析,实际上,通过抽象类 FileParser
,我们可以轻松地开发不同的文件解析器。
最后,让我们总结一下模板方法模式的使用场景:
-
算法的整体步骤非常固定,但当个别部分是可变的时,此时可以使用模板方法模式来抽象易变部分,以便子类实现。
欢迎关注公众号:文本魔术,了解更多