关于javascript:对象创建型设计模式Builder

Builder设计模式的目标就是将一个简单对象的构建与示意拆散,使同样的构建过程能够有不同的示意。

例如有一个XML文件解析程序,一种需要可能将一个xml文件转换成json格局的数据,而另一个需要须要将xml文件转换成csv文本模式。一种可能的实现形式就是实现一个独立的xml文本解析器,解析器从前往后解析文本,遇到不同的标签格局时告诉特定的构建器构建后果。解析实现时由构建器返回最终的构建后果。

上面咱们以创立一个简略的模板引擎为例理解builder模式:
对于模板引擎大家肯定很相熟,比方Mustache、pug、ejs,understore的template办法,这些模板的应用通常都是传入一段模板自字符串,返回一个编译好的函数,通过一个上下文对象作为参数调用这个函数就能够失去一段html文本字符串。

比方pug的应用形式如下(例子来自于pug中文文档):

const pug = require('pug');
const compiledFunction = pug.compileFile('template.pug');
console.log(compiledFunction({name: '李莉'}));
// "<p>李莉的 Pug 代码!</p>"
// 渲染另外一组数据
console.log(compiledFunction({name: '张伟'}));
// "<p>张伟的 Pug 代码!</p>"

接下来咱们来写一个本人的模板解析工具:
假如当初有一个这样的模板字符串:

    const tpl = `
        <p>{{name}}</p>
        {{if isVip}}
        <p>Welcome!</p>
        {{/endif}}
    `;
    

在下面的模板中{{}}示意一个插值,{{if isVip}} …{{/endif}}示意一个条件编译语句,只有在isVip为true时,之间的html内容才会被渲染。

首先定义一个解析器:

class Parser {
     static parse(tpl, builder) {
         const htmlReg = /^s*([^{}]+)/;
         const interpReg = /^s*{{s*(w+?)s*}}/;
         const ifReg = /^s*{{ifs+(w+?)s*}}(.*?){{/endif}}/;
         // 去除换行和多余的空格
         tpl = tpl.replace(/n+/g, '').replace(/s{2,}/g, ' ');
         while(tpl.length) {
             // 一般标签
             if (htmlReg.test(tpl)) {
                 const match = tpl.match(htmlReg);
                 const label = match[0];
                 tpl = tpl.substr(label.length);
                 // html += (html === '' ? '' : '+') + JSON.stringify(label.trim());
                 builder.html(label.trim());
             } else if(ifReg.test(tpl)) { // 条件语句
                 const match = tpl.match(ifReg);
                 const label = match[0];
                 tpl = tpl.substr(label.length);
                 builder.condition(...match.slice(1, 3).map(s => s.trim()));
             } else if(interpReg.test(tpl)) { // 插值语句
                 const match = tpl.match(interpReg);
                 const label = match[0];
                 const name = match[1];
                 tpl = tpl.substr(label.length);
                 // html += '+' + '$$option.' + name;
                 builder.interpolation(name);
             } else {
                break;
             }
         }
     }
}

解析器的有一个parse办法,该办法承受两个参数,一个待解析的模板字符串以及一个构建器,构建器须要提供至多三个办法html,interpolattion,condition这三个办法别离用于解决解析器解析进去的一般html、插值表达式、以及条件语句。依照雷同的形式能够扩大出其余更多的管制语句。
上面时builder类的实现:

class Builder {
     constructor(ctxName = '$$option') {
         this._result = '';
         this._ctxName = ctxName;
     }
     result() {
        return this._result;
     }
     html(html) {
        this._result += (this._result === '' ? '' : '+') + JSON.stringify(html);
     }
     condition(name, content) {
         this._result += this._result === '' ? '' : '+';
         this._result += `
         (${this._ctxName}.${name}?${JSON.stringify(content)}:${JSON.stringify('')})
         `;
     }
         interpolation(name) {
         this._result += (this._result === '' ? '' : '+') + `${this._ctxName}.` + name;
     }
}

Builder类的result办法返回最终失去的代码字符串。
联合下面的两个类,实现的编译函数如下:


function compile(tpl) {
     const builder = new Builder();
     Parser.parse(tpl, builder);
     return new Function('$$option', `return ${builder.result()}`);
}

compile函数首先构建一个builder实例,通过该实例调用parse办法,最终返回一个函数用于执行从builder失去的代码字符串。

接下来测试一下这个程序:

    const template = compile(tpl);
    console.log(template({name: '小明', isVip: true}));

执行后果如下:

    <p>小明</p><p>Welcome!</p>

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理