跳至主要內容

访问者模式

约 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);

上次编辑于: