2007 |
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.
Illustration
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 |
Section 1, Chapter 10 |
Design
2016 |
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
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);
}
}