天天看點

Design Pattern - Behavioral Patterns - Visitor Pattern

2007
Design Pattern - Behavioral Patterns - Visitor Pattern
Section 11, Chapter 3

Visitor Pattern

Concept

The Visitor pattern allows changes or additions to a class's structure without changing the actual class.

Use

The Visitor pattern passes other classes with the same method structure but different functionality into a class, and uses that passed-in class's method to change the class's behavior.

Design

This pattern has several components:

  • Visitor: the abstract base for the implementation classes that contain the functional methods;
  • Concrete Visitor: contains the actual functional method and controls which Element type is allowed to use this method;
  • Element: the abstract base for the class that actually contains the state we wish to modify;
  • Concrete Element: the implementation or instance of Element class;
  • Object Structure: provides a container that allows an enumeration of the different element classes that we will allow the visitors to interact with.
Design Pattern - Behavioral Patterns - Visitor Pattern

Illustration

Design Pattern - Behavioral Patterns - Visitor Pattern

The problem can be described by the code:

class Customer : Element
{
	public Customer(string name, double balance){...}
	public string Name{...}
	public double Balance{...}
	public void Credit(double amount)
	{
		_balance += amount;
	}
	public void Debit(double amount)
	{
		_balance -= amount;
	}
}

//transaction code
Customer customer = new Customer(customerName,balanceTotal);
//DB is our data layer
if(amount > 0)
	customer.Credit(DB.Credit(amount));
else
	customer.Debit(DB.Debit(amount));
//end transaction code
           

The solution can be:

abstract class Visitor
{
	public abstract void Visit(Element element);
}
abstract class Element
{
	public abstract void Accept(Visitor visitor);
}
class CreditVisitor : Visitor
{
	public CreditVisitor(double amount){...}
	public override void Visit(Element element)
	{
		Customer customer = (Customer)element;
		customer.Credit(DB.Credit(amount));
	}
}
class DebitVisitor : Visitor
{
	public DebitVisitor(double amount) {...}
	public override void Visit(Element element)
	{
		Customer customer = (Customer)element;
		customer.Debit(DB.Debit(amount));
	}
}

class Customer : Element
{ ... }


class Customers
{
	public void AttachElement(Customer customer)
	{
		_customers.Add(customer);
	}
	public void DetachElement(Customer customer)
	{
		_customers.Remove(customer);
	}
	public void AcceptVisitor(Visitor visitor)
	{
		foreach(Customer customer in _customers)
		{
			customer.Accept(visitor);
		}
	}
}

//transaction code
Customers customers = new Customers();

customers.AttachElement(new Customer("George", 233.50));
customers.AttachElement(new Customer("Janice", 102.25));
customers.AttachElement(new Customer("Richard", 2005.48));

CreditVisitor creditVisitor = new CreditVisitor(50.15);
DebitVisitor debitVisitor = new DebitVisitor(22.20);

customers.AcceptVisitor(creditVisitor);
customers.AcceptVisitor(debitVisitor);
           
Note: The very import method Accept() of Customer class has not been defined in the sample code.
December 2007
Design Pattern - Behavioral Patterns - Visitor Pattern
Section 1, Chapter 10

Design

Design Pattern - Behavioral Patterns - Visitor Pattern
2016
Design Pattern - Behavioral Patterns - Visitor Pattern
Chapter 23
package visitor.pattern.demo;

interface IOriginalInterface
{
	void accept(IVisitor visitor);
}

class MyClass implements IOriginalInterface
{
	//Initial or default value
	private int myInt = 5;
	public int getMyInt()
	{
		return myInt;
	}
	public void setMyInt(int myInt)
	{
		this.myInt = myInt;
	}
	@Override
	public void accept(IVisitor visitor)
	{
		System.out.println("Initial value of the integer :"+ myInt);
		visitor.visit(this);
		System.out.println("\nValue of the integer now :"+ myInt);
	}
}

interface IVisitor
{
	void visit(MyClass myClassElement);
}
class Visitor implements IVisitor
{
	@Override
	public void visit(MyClass myClassElement)
	{
		System.out.println("Visitor is trying to change the integer value");
		myClassElement.setMyInt(100);
		System.out.println("Exiting from Visitor- visit");
	}
}

class VisitorPatternEx
{
	public static void main(String[] args)
	{
		System.out.println("***Visitor Pattern Demo***\n");
		IVisitor v = new Visitor();
		MyClass myClass = new MyClass();
		myClass.accept(v);
	}
}
           

Vincent's Demonstration

Design Pattern - Behavioral Patterns - Visitor Pattern
import java.util.ArrayList;

public abstract class Visitor {
    public abstract void visit(Element element);
}

public class AnnualBonusCalculator extends Visitor {
    private final double FUND_BASE_RATE = 0.015;
    private final int NUMBER_OF_FACTOR = 3;
    @Override
    public void visit(Element element) {
        Employee emp = (Employee)element;
        double bonus = emp.getAnnualWage() * FUND_BASE_RATE * 
                        (emp.getFirstGoalGrade() + emp.getSecondGoalGrade() + 
                        emp.getIndividualPerformanceGrade()) / NUMBER_OF_FACTOR;
        System.out.println("Employee " + emp.getName() + " should get bonus: " + bonus);
    }
}

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

public class Employee extends Element{
    private String _name;
    private double _annualWage;
    private double _firstGoalGrade;
    private double _secondGoalGrade;
    private double _individualPerformanceGrade;
    public Employee(String name, double annualWage, double firstGoalGrade, 
                    double secondGoalGrade, double individualPerformanceGrade) {
        this._name = name;
        this._annualWage = annualWage;
        this._firstGoalGrade = firstGoalGrade;
        this._secondGoalGrade = secondGoalGrade;
        this._individualPerformanceGrade = individualPerformanceGrade;
    }
    public String getName() {
        return this._name;
    }
    public double getAnnualWage() {
        return this._annualWage;
    }
    public double getFirstGoalGrade() {
        return this._firstGoalGrade;
    }
    public double getSecondGoalGrade() {
        return this._secondGoalGrade;
    }
    public double getIndividualPerformanceGrade() {
        return this._individualPerformanceGrade;
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

public class Employees {
    private ArrayList<Employee> _employeeList = new ArrayList<Employee>();
    public void attachElement(Employee employee) {
        this._employeeList.add(employee);
    }
    public void detachElement(Employee employee)
    {
        this._employeeList.remove(employee);
    }
    public void acceptVisitor(Visitor visitor)
    {
        for (Employee employee : this._employeeList) {
            employee.accept(visitor);
        }
    }	
}

public class VisitorPatternDemo {
    public static void main(String[] args) {
        System.out.println("***Visitor Pattern Demo***"); 
        System.out.println();
        Employees employees = new Employees();
        employees.attachElement(new Employee("Julia Ann", 57233.00, 4.5, 5.7, 5.6));
        employees.attachElement(new Employee("Riley Steele", 87990.00, 6.5, 2.9, 5.0));
        employees.attachElement(new Employee("Jenna Haze", 45360.00, 5.1, 3.7, 5.3));
        Visitor calculator = new AnnualBonusCalculator();
        employees.acceptVisitor(calculator);
    }
}