访问者模式
约 610 字
访问者模式
核心思想
访问者模式允许将算法与对象结构分离,以便在不修改已有代码的情况下向现有类层次中添加新的行为。
典型用例
保证兼容性和扩展性
为保证兼容性和扩展性,当需要对一组不同类型的对象执行操作,且这些对象经常添加新类型时,访问者模式允许在不修改现有类的情况下添加新操作。
在这个例子中,有两种类型的元素和两种类型的访问者。每个访问者都以不同的方式处理不同类型的元素。如果需要添加新的元素类型或新的操作方式,只需添加新的元素类或新的访问者类,而无需修改现有的类。
// npm run code src/code/design-pattern/visitor/compatibility.ts
// 定义元素接口,用于接收访问者
interface MyElement {
accept(visitor: Visitor): void;
}
// 访问者接口
interface Visitor {
visitConcreteElementA(element: ConcreteElementA): void;
visitConcreteElementB(element: ConcreteElementB): void;
// 更多元素的访问方法可以在这里添加
}
// 创建具体元素
class ConcreteElementA implements MyElement {
accept(visitor: Visitor): void {
visitor.visitConcreteElementA(this);
}
operationA(): void {
console.log('ConcreteElementA operation.');
}
}
class ConcreteElementB implements MyElement {
accept(visitor: Visitor): void {
visitor.visitConcreteElementB(this);
}
operationB(): void {
console.log('ConcreteElementB operation.');
}
}
// 为不同操作创建具体访问者
class ConcreteVisitor1 implements Visitor {
visitConcreteElementA(element: ConcreteElementA): void {
console.log('ConcreteVisitor1 visiting ConcreteElementA.');
element.operationA();
}
visitConcreteElementB(element: ConcreteElementB): void {
console.log('ConcreteVisitor1 visiting ConcreteElementB.');
element.operationB();
}
}
class ConcreteVisitor2 implements Visitor {
visitConcreteElementA(element: ConcreteElementA): void {
console.log('ConcreteVisitor2 visiting ConcreteElementA.');
element.operationA();
}
visitConcreteElementB(element: ConcreteElementB): void {
console.log('ConcreteVisitor2 visiting ConcreteElementB.');
element.operationB();
}
}
const elements: MyElement[] = [
new ConcreteElementA(),
new ConcreteElementB()
];
const visitor1 = new ConcreteVisitor1();
const visitor2 = new ConcreteVisitor2();
elements.forEach(element => {
element.accept(visitor1); // 每个元素接受 ConcreteVisitor1
});
elements.forEach(element => {
element.accept(visitor2); // 每个元素接受 ConcreteVisitor2
});
解析文档或对象结构
访问者模式适用于操作复杂对象结构,例如:XML文档或语法树。这种模式允许在不修改对象结构的情况下,向其添加新的操作。当需要为元素添加新的操作时,只需添加新的访问者类,而无需修改现有的元素类。这种模式特别适合于对象结构经常变化,但操作相对固定的场景。
// npm run code src/code/design-pattern/visitor/parse-document-or-object-structure.ts
export {};
interface MyElement {
accept(visitor: Visitor): void;
}
class XMLNode implements MyElement {
accept(visitor: Visitor): void {
visitor.visitXMLNode(this);
}
// XMLNode 特有的方法
getTagName(): string {
return "XML Tag";
}
}
class JSONNode implements MyElement {
accept(visitor: Visitor): void {
visitor.visitJSONNode(this);
}
// JSONNode 特有的方法
getKeyName(): string {
return "JSON Key";
}
}
interface Visitor {
visitXMLNode(node: XMLNode): void;
visitJSONNode(node: JSONNode): void;
}
class SyntaxChecker implements Visitor {
visitXMLNode(node: XMLNode): void {
console.log("Checking syntax for XMLNode with tag: " + node.getTagName());
}
visitJSONNode(node: JSONNode): void {
console.log("Checking syntax for JSONNode with key: " + node.getKeyName());
}
}
class Renderer implements Visitor {
visitXMLNode(node: XMLNode): void {
console.log("Rendering XMLNode with tag: " + node.getTagName());
}
visitJSONNode(node: JSONNode): void {
console.log("Rendering JSONNode with key: " + node.getKeyName());
}
}
const xmlNode = new XMLNode();
const jsonNode = new JSONNode();
const syntaxChecker = new SyntaxChecker();
const renderer = new Renderer();
xmlNode.accept(syntaxChecker);
jsonNode.accept(renderer);
报告和统计
访问者模式允许在不改变对象的情况下添加新的报告功能,适用于需要从复杂对象集合中生成各种报告或统计信息。例如:不同的访问者可以用于计算总销售额、or平均值或其他统计数据。
// npm run code src/code/design-pattern/visitor/reporting-and-statistics.ts
export {};
interface SalesRecord {
accept(visitor: Visitor): void;
getAmount(): number;
}
class ConcreteSalesRecord implements SalesRecord {
constructor(private amount: number) {}
accept(visitor: Visitor): void {
visitor.visitSalesRecord(this);
}
getAmount(): number {
return this.amount;
}
}
interface Visitor {
visitSalesRecord(record: SalesRecord): void;
}
class TotalSalesVisitor implements Visitor {
private total = 0;
visitSalesRecord(record: SalesRecord): void {
this.total += record.getAmount();
}
getTotal(): number {
return this.total;
}
}
class AverageSalesVisitor implements Visitor {
private total = 0;
private count = 0;
visitSalesRecord(record: SalesRecord): void {
this.total += record.getAmount();
this.count++;
}
getAverage(): number {
return this.count === 0 ? 0 : this.total / this.count;
}
}
const records = [
new ConcreteSalesRecord(100),
new ConcreteSalesRecord(200),
new ConcreteSalesRecord(300),
];
const totalSalesVisitor = new TotalSalesVisitor();
const averageSalesVisitor = new AverageSalesVisitor();
records.forEach(record => {
record.accept(totalSalesVisitor);
record.accept(averageSalesVisitor);
});
console.log("Total Sales: " + totalSalesVisitor.getTotal());
console.log("Average Sales: " + averageSalesVisitor.getAverage());
操作用户界面组件
访问者模式可以在用户界面组件上执行不同的操作,例如:布局、绘制和事件处理。这种模式允许在不修改组件类的情况下,为组件添加新的操作。
// npm run code src/code/design-pattern/visitor/user-interface-operation.ts
export {};
interface GUIComponent {
accept(visitor: GUIVisitor): void;
}
class Button implements GUIComponent {
accept(visitor: GUIVisitor): void {
visitor.visitButton(this);
}
// Button 特有的方法
click(): void {
console.log("Button clicked");
}
}
class TextBox implements GUIComponent {
accept(visitor: GUIVisitor): void {
visitor.visitTextBox(this);
}
// TextBox 特有的方法
setText(text: string): void {
console.log(`Text set in TextBox: ${text}`);
}
}
interface GUIVisitor {
visitButton(button: Button): void;
visitTextBox(textBox: TextBox): void;
}
class LayoutVisitor implements GUIVisitor {
visitButton(button: Button): void {
console.log("Laying out button");
}
visitTextBox(textBox: TextBox): void {
console.log("Laying out text box");
}
}
class RenderVisitor implements GUIVisitor {
visitButton(button: Button): void {
console.log("Rendering button");
}
visitTextBox(textBox: TextBox): void {
console.log("Rendering text box");
}
}
class EventHandlingVisitor implements GUIVisitor {
visitButton(button: Button): void {
button.click();
console.log("Handling button click event");
}
visitTextBox(textBox: TextBox): void {
textBox.setText("Hello");
console.log("Handling text box text change event");
}
}
const button = new Button();
const textBox = new TextBox();
const layoutVisitor = new LayoutVisitor();
const renderVisitor = new RenderVisitor();
const eventHandlingVisitor = new EventHandlingVisitor();
button.accept(layoutVisitor);
button.accept(renderVisitor);
button.accept(eventHandlingVisitor);
textBox.accept(layoutVisitor);
textBox.accept(renderVisitor);
textBox.accept(eventHandlingVisitor);
序列化和反序列化
访问者模式允许将序列化和反序列化逻辑从对象本身分离,从而使对象类保持简洁且专注于其主要职责。
// npm run code src/code/design-pattern/visitor/serialization-and-deserialization.ts
export {};
interface SerializableObject {
accept(visitor: SerializationVisitor): void;
}
class Person implements SerializableObject {
constructor(public name: string, public age: number) {}
accept(visitor: SerializationVisitor): string {
return visitor.visitPerson(this);
}
}
class Address implements SerializableObject {
constructor(public street: string, public city: string) {}
accept(visitor: SerializationVisitor): string {
return visitor.visitAddress(this);
}
}
interface SerializationVisitor {
visitPerson(person: Person): string;
visitAddress(address: Address): string;
}
class JSONSerializationVisitor implements SerializationVisitor {
visitPerson(person: Person): string {
return JSON.stringify({name: person.name, age: person.age});
}
visitAddress(address: Address): string {
return JSON.stringify({street: address.street, city: address.city});
}
}
const person = new Person("Alice", 30);
const address = new Address("123 Main St", "Wonderland");
const jsonVisitor = new JSONSerializationVisitor();
const serializedPerson = person.accept(jsonVisitor);
const serializedAddress = address.accept(jsonVisitor);
console.log("Serialized Person: ", serializedPerson);
console.log("Serialized Address: ", serializedAddress);