The Visitor pattern is often maligned as too complex. I disagree. I use the visitor pattern all the time. I find it to be a very nice way to separate things that change for different reasons.
For example, let’s say I’ve got a list of employees and I want to create the following report of hourly employees:
Emp# | Name | QTD-hours | QTD-pay |
1429 | Bob Martin | 432 | $22,576 |
1532 | James Grenning | 490 $28,776 |
…
I could simply add a method to the Employee
class that produced that employee’s corresponding line on the report.
public class Employee {
public abstract String reportQtdHoursAndPay();
}
public class HourlyEmployee extends Employee {
public String reportQtdHoursAndPay() {
//generate the line for this hourly employee
}
}
public class SalariedEmployee extends Employee {
public String reportQtdHoursAndPay() {} // do nothing
}
Then I could generate the report by iterating over all the Employee
objects and calling reportQtdHoursAndPay()
.
The problem with this is that we have coupled the format and content of a report to the Employee
object. This violates the Single Reponsibility Principle because it causes Employee
to be changed every time the format or content of the report changes, or when any new reports are added.
To fix this, I can use the Visitor pattern as follows:
public class Employee {
public abstract void accept(EmployeeVisitor v);
}
public class HourlyEmployee extends Employee {
public void accept(EmployeeVisitor v) {
v.visit(this);
}
}
interface EmployeeVisitor {
public void visit(HourlyEmployee he);
public void visit(SalariedEmployee se);
}
public class QtdHoursAndPayReport implements EmployeeVisitor {
public void visit(HourlyEmployee he) {
// generate the line of the report.
}
public void visit(SalariedEmployee se) {} // do nothing
}
Now I can generate the report by creating a QtdHoursAndPayReport
instance, and then iterating over all the Employee
instances and calling accept and passing in the visitor:
QtdHoursAndPayReport v = new QtdHoursAndPayReport();
for (...) // each employee e
{
e.accept(v);
}
This solves the Single Responsibility Principle by moving the report generation into a new object. Moreover, each new report will be added to a new derivative of EmployeeVisitor
.
这个例子很好,其实要使用到访问者模式的地方都是有两个条件的,第一对于一群无关的类需要实施一类相同的算法;第二这群类必须要放到一个集合中