现在的位置: 首页 > 综合 > 正文

设计模式之visitor访问者模式

2013年10月26日 ⁄ 综合 ⁄ 共 7844字 ⁄ 字号 评论关闭

意图intent:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

适用性:

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。

  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作污染这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。

  • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

Definition

Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

participants

    The classes and/or objects participating in this pattern are:

  • Visitor  (Visitor)

    • declares a Visit operation for each class of ConcreteElement in the object structure. The operation's name and signature identifies the class that sends the Visit request to the visitor. That lets the visitor determine the concrete class of the element being visited. Then the visitor can access the elements directly through its particular interface

  • ConcreteVisitor  (IncomeVisitor, VacationVisitor)

    • implements each operation declared by Visitor. Each operation implements a fragment of the algorithm defined for the corresponding class or object in the structure. ConcreteVisitor provides the context for the algorithm and stores its local state. This state often accumulates results during the traversal of the structure.

  • Element  (Element)

    • defines an Accept operation that takes a visitor as an argument.

  • ConcreteElement  (Employee)

    • implements an Accept operation that takes a visitor as an argument

  • ObjectStructure  (Employees)

    • can enumerate its elements

    • may provide a high-level interface to allow the visitor to visit its elements

    • may either be a Composite (pattern) or a collection such as a list or a set

在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

Visitor模式通过所谓双重分发(double dispatch)来实现在不更改element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。所谓双重分发即visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。Visitor模式的最大缺点在于扩展类层次结构(增添新的element子类),会导致visitor类的改变。因此visitor模式适用于element类层次结构稳定,而其中的操作却经常面临频繁改动。

 

Sample code in c#

This structural code demonstrates the Visitor pattern in which an object traverses an object structure and performs the same operation on each node in this structure. Different visitor objects define different operations.

// Visitor pattern -- Structural example

 

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Visitor.Structural
{
  
  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      // Setup structure
      ObjectStructure o = new ObjectStructure();
      o.Attach(new ConcreteElementA());
      o.Attach(new ConcreteElementB());

      // Create visitor objects
      ConcreteVisitor1 v1 = new ConcreteVisitor1();
      ConcreteVisitor2 v2 = new ConcreteVisitor2();

      // Structure accepting visitors
      o.Accept(v1);
      o.Accept(v2);

      // Wait for user
      Console.Read();
    }
  }

  // "Visitor"

  abstract class Visitor
  {
    public abstract void VisitConcreteElementA(
      ConcreteElementA concreteElementA);
    public abstract void VisitConcreteElementB(
      ConcreteElementB concreteElementB);
  }

  // "ConcreteVisitor1"

  class ConcreteVisitor1 : Visitor
  {
    public override void VisitConcreteElementA(
      ConcreteElementA concreteElementA)
    {
      Console.WriteLine("{0} visited by {1}",
        concreteElementA.GetType().Name, this.GetType().Name);
    }

    public override void VisitConcreteElementB(
      ConcreteElementB concreteElementB)
    {
      Console.WriteLine("{0} visited by {1}",
        concreteElementB.GetType().Name, this.GetType().Name);
    }
  }

  // "ConcreteVisitor2"

  class ConcreteVisitor2 : Visitor
  {
    public override void VisitConcreteElementA(
      ConcreteElementA concreteElementA)
    {
      Console.WriteLine("{0} visited by {1}",
        concreteElementA.GetType().Name, this.GetType().Name);
    }

    public override void VisitConcreteElementB(
      ConcreteElementB concreteElementB)
    {
      Console.WriteLine("{0} visited by {1}",
        concreteElementB.GetType().Name, this.GetType().Name);
    }
  }

  // "Element"

  abstract class Element
  {
    public abstract void Accept(Visitor visitor);
  }

  // "ConcreteElementA"

  class ConcreteElementA : Element
  {
    public override void Accept(Visitor visitor)
    {
      visitor.VisitConcreteElementA(this);
    }

    public void OperationA()
    {
    }
  }

  // "ConcreteElementB"

  class ConcreteElementB : Element
  {
    public override void Accept(Visitor visitor)
    {
      visitor.VisitConcreteElementB(this);
    }

    public void OperationB()
    {
    }
  }

  // "ObjectStructure"

  class ObjectStructure
  {
    private ArrayList elements = new ArrayList();

    public void Attach(Element element)
    {
      elements.Add(element);
    }

    public void Detach(Element element)
    {
      elements.Remove(element);
    }

    public void Accept(Visitor visitor)
    {
      foreach (Element e in elements)
      {
        e.Accept(visitor);
      }
    }
  }
}

 

Output

ConcreteElementA visited by ConcreteVisitor1

ConcreteElementB visited by ConcreteVisitor1

ConcreteElementA visited by ConcreteVisitor2

ConcreteElementB visited by ConcreteVisitor2

 

This real-world code demonstrates the Visitor pattern in which two objects traverse a list of Employees and performs the same operation on each Employee. The two visitor objects define different operations -- one adjusts vacation days and the other income.

// Visitor pattern -- Real World example

 

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Visitor.RealWorld
{
  
  // MainApp startup application

  class MainApp
  {
    static void Main()
    {
      // Setup employee collection
      Employees e = new Employees();
      e.Attach(new Clerk());
      e.Attach(new Director());
      e.Attach(new President());

      // Employees are 'visited'
      e.Accept(new IncomeVisitor());
      e.Accept(new VacationVisitor());

      // Wait for user
      Console.Read();
    }
  }

  // "Visitor"

  interface IVisitor
  {
    void Visit(Element element);
  }

  // "ConcreteVisitor1"

  class IncomeVisitor : IVisitor
  {
    public void Visit(Element element)
    {
      Employee employee = element as Employee;

      // Provide 10% pay raise
      employee.Income *= 1.10;
      Console.WriteLine("{0} {1}'s new income: {2:C}",
        employee.GetType().Name, employee.Name,
        employee.Income);
    }
  }

  // "ConcreteVisitor2"

  class VacationVisitor : IVisitor
  {
    public void Visit(Element element)
    {
      Employee employee = element as Employee;
      
      // Provide 3 extra vacation days
      Console.WriteLine("{0} {1}'s new vacation days: {2}",
        employee.GetType().Name, employee.Name,
        employee.VacationDays);
    }
  }

  class Clerk : Employee
  {
    // Constructor
    public Clerk() : base("Hank", 25000.0, 14)
    {
    }
  }

  class Director : Employee
  {
    // Constructor
    public Director() : base("Elly", 35000.0, 16)
    {  
    }
  }

  class President : Employee
  {
    // Constructor
    public President() : base("Dick", 45000.0, 21)
    {  
    }
  }

  // "Element"

  abstract class Element
  {
    public abstract void Accept(IVisitor visitor);
  }

  // "ConcreteElement"

  class Employee : Element
  {
    string name;
    double income;
    int vacationDays;

    // Constructor
    public Employee(string name, double income,
      int vacationDays)
    {
      this.name = name;
      this.income = income;
      this.vacationDays = vacationDays;
    }

    // Properties
    public string Name
    {
      get{ return name; }
      set{ name = value; }
    }

    public double Income
    {
      get{ return income; }
      set{ income = value; }
    }

    public int VacationDays
    {
      get{ return vacationDays; }
      set{ vacationDays = value; }
    }

    public override void Accept(IVisitor visitor)
    {
      visitor.Visit(this);
    }
  }

  // "ObjectStructure"

  class Employees
  {
    private ArrayList employees = new ArrayList();

    public void Attach(Employee employee)
    {
      employees.Add(employee);
    }

    public void Detach(Employee employee)
    {
      employees.Remove(employee);
    }

    public void Accept(IVisitor visitor)
    {
      foreach (Employee e in employees)
      {
        e.Accept(visitor);
      }
      Console.WriteLine();
    }
  }
}

 

Output

Clerk Hank's new income: $27,500.00

Director Elly's new income: $38,500.00

President Dick's new income: $49,500.00

 

Clerk Hank's new vacation days: 14

Director Elly's new vacation days: 16

President Dick's new vacation days: 21

 

抱歉!评论已关闭.