通路者模式
Visitor Pattern
中屬于行為型模式,針對于對象結構中的元素,定義在不改變該對象的前提下通路結構中元素的新方法,元素的執行算法可以随着通路者改變而改變,主要意圖在于将資料結構與資料操作分離,解決穩定的資料結構和易變的操作耦合問題。
描述
在面向對象程式設計和軟體工程中,通路者設計模式是一種将算法與它所運作的對象結構分離的方法,這種分離的實際結果是能夠在不修改現有對象結構的情況下向現有對象結構添加新操作,這是遵循開放封閉原則的一種方式。
優點
- 符合單一職責原則,凡是适用通路者模式的場景中,元素類中需要封裝在通路者中的操作必定是與元素類本身關系不大且是易變的操作,使用通路者模式一方面符合單一職責原則,另一方面,因為被封裝的操作通常來說都是易變的,是以當發生變化時,就可以在不改變元素類本身的前提下,實作對變化部分的擴充。
- 擴充性良好,元素類可以通過接受不同的通路者來實作對不同操作的擴充。
缺點
- 具體元素對通路者公布細節,違反了迪米特原則。
- 具體元素變更比較困難。
- 違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。
适用環境
- 假如一個對象中存在着一些與本對象不相幹(或者關系較弱)的操作,為了避免這些操作污染這個對象,則可以使用通路者模式來把這些操作封裝到通路者中去。
- 假如一組對象中,存在着相似的操作,為了避免出現大量重複的代碼,也可以将這些重複的操作封裝到通路者中去。
實作
// 以動物園模拟為例,我們有幾種不同種類的動物,它們能夠發出不同的聲音。
class Monkey {
shout() {
console.log("Ooh oo aa aa!");
}
accept(operation) {
operation.visitMonkey(this);
}
}
class Lion {
roar() {
console.log("Roaaar!");
}
accept(operation) {
operation.visitLion(this);
}
}
class Dolphin {
speak() {
console.log("Tuut tuttu tuutt!");
}
accept(operation) {
operation.visitDolphin(this);
}
}
const speak = {
visitMonkey(monkey) {
monkey.shout();
},
visitLion(lion) {
lion.roar();
},
visitDolphin(dolphin) {
dolphin.speak();
}
}
;(function(){
const monkey = new Monkey();
const lion = new Lion();
const dolphin = new Dolphin();
monkey.accept(speak); // Ooh oo aa aa!
lion.accept(speak); // Roaaar!
dolphin.accept(speak); // Tuut tutt tuutt!
})();
// 我們可以通過對動物具有繼承層次結構來簡單地做到這一點
// 但是每當必須向動物添加新動作時,我們就必須修改動物。
// 而現在我們不必更改它們。
// 例如,假設我們被要求将跳躍行為添加到動物中,我們可以簡單地通過建立一個新的訪客來添加它。
const jump = {
visitMonkey(monkey) {
console.log("Jumped 20 feet high! on to the tree!");
},
visitLion(lion) {
console.log("Jumped 7 feet! Back on the ground!");
},
visitDolphin(dolphin) {
console.log("Walked on water a little and disappeared");
}
}
;(function(){
const monkey = new Monkey();
const lion = new Lion();
const dolphin = new Dolphin();
monkey.accept(speak); // Ooh oo aa aa!
monkey.accept(jump); // Jumped 20 feet high! on to the tree!
lion.accept(speak); // Roaaar!
lion.accept(jump); // Jumped 7 feet! Back on the ground!
dolphin.accept(speak); // Tuut tutt tuutt!
dolphin.accept(jump); // Walked on water a little and disappeared
})();
每日一題
https://github.com/WindrunnerMax/EveryDay
參考
https://www.runoob.com/design-pattern/visitor-pattern.html
https://github.com/sohamkamani/javascript-design-patterns-for-humans#-visitor
https://www.bookstack.cn/read/design-pattern-in-javascript/design-pattern-visitor-pattern-README.md