過濾器模式又被稱爲標準模式,這種模式主要使用不同標準來過濾一組對象。過濾的過程便是一個層層篩選的過程,因此過濾器模式屬於結構型設計模式的一種。
由於實際開發中過濾器模式的使用方式往往要和責任鏈模式結合使用。所以這裏也需要一併介紹下責任鏈模式。
責任鏈模式顧名思義就是創建一個鏈條,經過這個鏈條處理的所有對象和數據分別進行依次加工,每個環節負責處理不同的業務,環節間彼此獨立解耦,同時可以複用。
使用過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入門(三)