过滤器模式又被称为标准模式,这种模式主要使用不同标准来过滤一组对象。过滤的过程便是一个层层筛选的过程,因此过滤器模式属于结构型设计模式的一种。
由于实际开发中过滤器模式的使用方式往往要和责任链模式结合使用。所以这里也需要一并介绍下责任链模式。
责任链模式顾名思义就是创建一个链条,经过这个链条处理的所有对象和数据分别进行依次加工,每个环节负责处理不同的业务,环节间彼此独立解耦,同时可以复用。
使用过gulp的同学应该很好理解责任链,因为gulp本身的文件处理机制便是基于责任链模式设计,使用链式调用,不同的loader负责对文件做不同的处理工作。实际webpack也是基于这种模式进行处理的,只是将调用和实例化的过程省略,只保留了loader和plugin的配置。
场景模拟
假设我们通过客户端的富文本编辑器编辑新闻或论坛内容,但是后台需要按以下规则对所提交的内容进行过滤:
- 为文章自动添加标签
- 对可能包含的特殊字符需要屏蔽或替换
- 对色情,暴力,犯罪等敏感词汇进行替换
- 所有提交内容的结尾添加文章来源和时间描述
代码示例
为了更好的模拟基于面向对象的写法,示例代码使用 TypeScript.
class Msg {
msg: string;
setMsg(msg): void {
this.msg = msg;
}
getMsg(): string {
return this.msg;
}
}
interface Filter {
doFilter(m: Msg): boolean;
}
// 过滤危险html标签
class HtmlFilter implements Filter {
doFilter(m: Msg) {
let r: string = m.getMsg();
r = r.replace('<', '[');
r = r.replace('>', ']');
m.setMsg(r);
return true;
}
}
// 添加热门标签
class HotFilter implements Filter {
doFilter(m: Msg) {
let r: string = m.getMsg();
r = "\n Hot News:\n \n" + r;
m.setMsg(r);
return true;
}
}
// 补充消息时间和来源
class SourceWhenFilter implements Filter {
doFilter(m: Msg) {
let r: string = m.getMsg();
r = r + "\n\nSource from CNN, ";
r = r + this.getDateStr();
m.setMsg(r);
return true;
}
getDateStr(): string {
let d: Date = new Date(),
YYYY = d.getFullYear(),
MM = d.getMonth() + 1,
dd = d.getDate(),
hh = d.getHours(),
mm = d.getMinutes();
str = `${YYYY}-${MM >= 10 ? MM : '0' + MM}-${dd >= 10 ? dd : '0' + dd} ${hh >= 10 ? hh : '0' + hh}:${mm >= 10 ? mm : '0' + mm}`;
return str;
}
}
// 过滤敏感词
class SensitiveFilter implements Filter {
doFilter(m: Msg) {
let r: string = m.getMsg();
r = r.replace("kill", "murder");
r = r.replace("riots", "parade");
m.setMsg(r);
return true;
}
}
class FilterChain implements Filter {
filters: Array<Filter> = [];
doFilter(m: Msg) {
for (let f of this.filters) {
if (!f.doFilter(m)) return false;
}
return true;
}
add(f: Filter): FilterChain {
this.filters.push(f);
return this;
}
}
let msgObj: Msg = new Msg();
msgObj.setMsg("<Police kneel down to kill black people in America which have Leading to continued riots in the United States.>");
let fc: FilterChain = new FilterChain();
fc.add(new HtmlFilter())
.add(new SensitiveFilter());
let fc2: FilterChain = new FilterChain();
fc2.add(new HotFilter())
.add(new SourceWhenFilter());
fc.add(fc2);
fc.doFilter(msgObj);
let str: string = msgObj.getMsg();
console.log(str);
执行结果:
这种设计的巧妙之处在于可以链式调用,不同的过滤方式可以灵活的排序和组合。既可以使用单个过滤器进行处理,也可以直接添加一条责任链。如上HtmlFilter可以被fc添加 ,fc2也可以直接被fc添加,因为他们都继承了Filter类,所以返回的数据类型相同,这种巧妙的差异在JavaScript中比较难以体现,这也是我们使用TypeScript作为示例代码的原因。
相比于过滤器,责任链是一种非常常见和重要的设计模式,因为它提供了一种可扩展的数据或对象处理方案,你可以基于任何规则编写自己的一个插件,只需要遵守责任链的定义方式。如果你想尝试编写自己的插件,不妨先去实现一个简单的gulp插件。具体的实现方式可以查看相关博客 自动化构建工具gulp入门(三)