訪問者模式

訪問者模式是一種將算法與對象結構分離的軟件設計模式

Visitor, UML class diagram.

這個模式的基本想法如下:首先我們擁有一個由許多對象構成的對象結構,這些對象的都擁有一個accept方法用來接受訪問者對象;訪問者是一個接口,它擁有一個visit方法,這個方法對訪問到的對象結構中不同類型的元素作出不同的反應;在對象結構的一次訪問過程中,我們遍歷整個對象結構,對每一個元素都實施accept方法,在每一個元素的accept方法中回調訪問者的visit方法,從而使訪問者得以處理對象結構的每一個元素。我們可以針對對象結構設計不同的實在的訪問者類來完成不同的操作。

訪問者模式使得我們可以在傳統的單分派語言(如SmalltalkJavaC++)中模擬雙分派技術。對於支持多分派的語言(如CLOS),訪問者模式已經內置於語言特性之中了,從而不再重要。

Python的例子

"""
Visitor pattern example.
"""

from abc import ABCMeta, abstractmethod

NOT_IMPLEMENTED = "You should implement this."

class CarElement:
    __metaclass__ = ABCMeta
    @abstractmethod
    def accept(self, visitor):
        raise NotImplementedError(NOT_IMPLEMENTED)

class Body(CarElement):
    def accept(self, visitor):
        visitor.visitBody(self)


class Engine(CarElement):
    def accept(self, visitor):
        visitor.visitEngine(self)


class Wheel(CarElement):
    def __init__(self, name):
        self.name = name
    def accept(self, visitor):
        visitor.visitWheel(self)

class Car(CarElement):
    def __init__(self):
        self.elements = [
            Wheel("front left"), Wheel("front right"),
            Wheel("back left"), Wheel("back right"),
            Body(), Engine()
        ]

    def accept(self, visitor):
        for element in self.elements:
            element.accept(visitor)
        visitor.visitCar(self)

class CarElementVisitor:
    __metaclass__ = ABCMeta
    @abstractmethod
    def visitBody(self, element):
        raise NotImplementedError(NOT_IMPLEMENTED)
    @abstractmethod
    def visitEngine(self, element):
        raise NotImplementedError(NOT_IMPLEMENTED)
    @abstractmethod
    def visitWheel(self, element):
        raise NotImplementedError(NOT_IMPLEMENTED)
    @abstractmethod
    def visitCar(self, element):
        raise NotImplementedError(NOT_IMPLEMENTED)

class CarElementDoVisitor(CarElementVisitor):
    def visitBody(self, body):
        print("Moving my body.")
    def visitCar(self, car):
        print("Starting my car.")
    def visitWheel(self, wheel):
        print("Kicking my {} wheel.".format(wheel.name))
    def visitEngine(self, engine):
        print("Starting my engine.")


class CarElementPrintVisitor(CarElementVisitor):
    def visitBody(self, body):
        print("Visiting body.")
    def visitCar(self, car):
        print("Visiting car.")
    def visitWheel(self, wheel):
        print("Visiting {} wheel.".format(wheel.name))
    def visitEngine(self, engine):
        print("Visiting engine.")

car = Car()
car.accept(CarElementPrintVisitor())
car.accept(CarElementDoVisitor())

Java的例子

 interface Visitor {
     void visit(Wheel wheel);
     void visit(Engine engine);
     void visit(Body body);
     void visit(Car car);
 }

 class Wheel {
     private String name;
     Wheel(String name) {
         this.name = name;
     }
     String getName() {
         return this.name;
     }
     void accept(Visitor visitor) {
         visitor.visit(this);
     }
 }
  
 class Engine {
     void accept(Visitor visitor) {
         visitor.visit(this);
     }
 }

 class Body {
     void accept(Visitor visitor) {
         visitor.visit(this);
     }
 }

 class Car {
     private Engine  engine = new Engine();
     private Body    body   = new Body();
     private Wheel[] wheels 
         = { new Wheel("front left"), new Wheel("front right"),
             new Wheel("back left") , new Wheel("back right")  };
     void accept(Visitor visitor) {
         visitor.visit(this);
         engine.accept(visitor);
         body.accept(visitor);
         for (int i = 0; i < wheels.length; ++ i)
             wheels[i].accept(visitor);
     }
 }

 class PrintVisitor implements Visitor {
     public void visit(Wheel wheel) {
         System.out.println("Visiting " + wheel.getName()
                             + " wheel");
     }
     public void visit(Engine engine) {
         System.out.println("Visiting engine");
     }
     public void visit(Body body) {
         System.out.println("Visiting body");
     }
     public void visit(Car car) {
         System.out.println("Visiting car");
     }
 }

 public class VisitorDemo {
     static public void main(String[] args) {
         Car car = new Car();
         Visitor visitor = new PrintVisitor();
         car.accept(visitor);
     }
 }

一個實際的例子

這個例子是Htmlparser計劃頁面存檔備份,存於網際網路檔案館)里的一段示例。為獲得一個Web頁面的所有內容,採用如下的方式使用類 TextExtractingVisitor:

Parser parser = new Parser("http://pageIwantToParse.com[永久失效連結]");
TextExtractingVisitor visitor = new TextExtractingVisitor();
parser.visitAllNodesWith(visitor);
System.out.println(visitor.getExtractedText());

參考條目