这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战
访问者模式
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
模式结构
- 抽象访问者
- 具体访问者
- 抽象元素
- 具体元素
- 对象结构
// 抽象组件
interface Component {
accept(visitor: Visitor): void;
}
class ComponentA implements Component {
public accept(visitor: Visitor): void {
visitor.visitA(this);
}
public getCompA(): string {
return 'A';
}
}
class ComponentB implements Component {
public accept(visitor: Visitor): void {
visitor.visitB(this);
}
public getCompB(): string {
return 'B';
}
}
//抽象访问者
interface Visitor {
visitA(element: ComponentA): void;
visitB(element: ComponentB): void;
}
// 访问者1
class Visitor1 implements Visitor {
public visitA(element: ComponentA): void {
console.log(`${element.getCompA()} + ConcreteVisitor1`);
}
public visitB(element: ComponentB): void {
console.log(`${element.getCompB()} + ConcreteVisitor1`);
}
}
// 访问者2
class Visitor2 implements Visitor {
public visitA(element: ComponentA): void {
console.log(`${element.getCompA()} + ConcreteVisitor2`);
}
public visitB(element: ComponentB): void {
console.log(`${element.getCompB()} + ConcreteVisitor2`);
}
}
// 对象结构,提供让访问者对象遍历容器中的所有元素的方法
function clientCode(components: Component[], visitor: Visitor) {
// ...
for (const component of components) {
component.accept(visitor);
}
// ...
}
(()=>{
const components = [
new ComponentA(),
new ComponentB(),
];
const visitor1 = new Visitor1();
clientCode(components, visitor1);
const visitor2 = new Visitor2();
clientCode(components, visitor2);
})
主要优点
- 在不修改对象结构中的元素,可给对象结构中的元素添加新功能,扩展性好
- 可通过访问者定义整个对象结构的功能,提高系统的复用程度
- 访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可自由演化不影响系统
- 把相关行为封装在一起,构成访问者,每个访问者功能单一符合单一职责
主要缺点
- 增加新元素时每个访问者类都需要增加相应的操作,违背开闭原则
- 访问者模式中具体元素对访问者公布细节,破坏对象封装性
- 访问者依赖具体类,没有依赖抽象类,违反依赖倒置原则
适用场景
- 对象结构相对稳定,但其操作算法经常变化的程序。
- 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
- 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。