https://sourcemaking.com/design_patterns/visitor
Intent
Represents 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.
The participants classes in this pattern are:
Example 1 - Customers Application
Visitors and Iterators
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:
I could simply add a method to the Employee class that produced that employee's corresponding line on the report.
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 Principlebecause 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:
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:
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
javax.lang.model.element.AnnotationValue obviously use Visitor pattern, but it is not commonly used in regular projects.
http://www.cnblogs.com/java-my-life/archive/2012/06/14/2545381.html
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
问题的核心就是Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,而不知道对象的真实类型;而方法的调用则是根据对象的真实类型,而不是静态类型。
Intent
- 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.
- The classic technique for recovering lost type information.
- Do the right thing based on the type of two objects.
- Double dispatch
The Visitor is known as a behavioural pattern,as it's used to manage algorithms, relationships and responsibilities between objects.
Allows for one or more operation to be applied to a set of objects at runtime, decoupling the operations from the object structure.
What the Visitor pattern actually does is create an external class that uses data in the other classes. If you need to perform operations across a dispate set of objects, Visitor might be the pattern for you. The GoF book says that the Visitor pattern can provide additional functionality to a class without changing it.
The pattern should be used when you have distinct and unrelated operations to perform across a structure of objects. This avoids adding in code throughout your object structure that is better kept seperate, so it encourages cleaner code. You may want to run operations against a set of objects with different interfaces. Visitors are also valuable if you have to perform a number of unrelated operations across the classes.
public class PostageVisitor implements Visitor {
private double totalPostageForCart;
//collect data about the book
public void visit(Book book) {
//assume we have a calculation here related to weight and price
//free postage for a book over 10
if(book.getPrice() < 10.0) {
totalPostageForCart += book.getWeight() * 2;
}
}
//add other visitors here
public void visit(CD cd) {...}
public void visit(DVD dvd) {...}
//return the internal state
public double getTotalPostage() {
return totalPostageForCart;
}
}
public class ShoppingCart {
//normal shopping cart stuff
private ArrayList<Visitable> items;
public double calculatePostage() {
//create a visitor
PostageVisitor visitor = new PostageVisitor();
//iterate through all items
for(Visitable item: items) {
item.accept(visitor);
}
double postage = visitor.getTotalPostage();
return postage;
}
}
Collections are data types widely used in object oriented programming. Often collections contain objects of different types and in those cases some operations have to be performed on all the collection elements without knowing the type.
A possible approach to apply a specific operation on objects of different types in a collection would be the use if blocks in conjunction with 'instanceof' for each element. This approach is not a nice one, not flexible and not object oriented at all. At this point we should think to the Open Close principle and we should remember from there that we can replace if blocks with an abstract class and each concrete class will implement its own operation.
A possible approach to apply a specific operation on objects of different types in a collection would be the use if blocks in conjunction with 'instanceof' for each element. This approach is not a nice one, not flexible and not object oriented at all. At this point we should think to the Open Close principle and we should remember from there that we can replace if blocks with an abstract class and each concrete class will implement its own operation.
Many distinct and unrelated operations need to be performed on node objects in a heterogeneous aggregate structure. You want to avoid "polluting" the node classes with these operations. And, you don't want to have to query the type of each node and cast the pointer to the correct type before performing the desired operation.
Visitor's primary purpose is to abstract functionality that can be applied to an aggregate hierarchy of "element" objects. The approach encourages designing lightweight Element classes - because processing functionality is removed from their list of responsibilities. New functionality can easily be added to the original inheritance hierarchy by creating a new Visitor subclass.
Visitor implements "double dispatch". OO messages routinely manifest "single dispatch" - the operation that is executed depends on: the name of the request, and the type of the receiver. In "double dispatch", the operation executed depends on: the name of the request, and the type of TWO receivers (the type of the Visitor and the type of the element it visits).
- Visitor - This is an interface or an abstract class used to declare the visit operations for all the types of visitable classes. Usually the name of the operation is the same and the operations are differentiated by the method signature: The input object type decides which of the method is called.
- ConcreteVisitor - For each type of visitor all the visit methods, declared in abstract visitor, must be implemented. Each Visitor will be responsible for different operations. When a new visitor is defined it has to be passed to the object structure.
- Visitable - is an abstraction which declares the accept operation. This is the entry point which enables an object to be "visited" by the visitor object. Each object from a collection should implement this abstraction in order to be able to be visited.
- ConcreteVisitable - Those classes implements the Visitable interface or class and defines the accept operation. The visitor object is passed to this object using the accept operation.
- ObjectStructure - This is a class containing all the objects that can be visited. It offers a mechanism to iterate through all the elements. This structure is not necessarily a collection. In can be a complex structure, such as a composite object.
Applicability & Examples
The visitor pattern is used when:
- Similar operations have to be performed on objects of different types grouped in a structure (a collection or a more complex structure).
- There are many distinct and unrelated operations needed to be performed. Visitor pattern allows us to create a separate visitor concrete class for each type of operation and to separate this operation implementation from the objects structure.
- The object structure is not likely to be changed but is very probable to have new operations which have to be added. Since the pattern separates the visitor (representing operations, algorithms, behaviors) from the object structure it's very easy to add new visitors as long as the structure remains unchanged.
Example 1 - Customers Application
Tight Coupled Visitable objects
The classic implementation of the Visitor pattern have a major drawback because the type of visitor methods has to be known in advance. The Visitor interface can be defined using polymorphic methods or methods with different names:
public interface IVisitor { public void visit(Customer customer); public void visit(Order order); public void visit(Item item); } public interface IVisitor { public void visitCustomer(Customer customer); public void visitOrder(Order order); public void visitItem(Item item); } |
However this type should be known in advance. When a new type is added to the structure a new method should be added to this interface and all existing visitors have to be changed accordingly. A pair method is written in the concrete Visitable objects:
public class Customer implements IVisitable{ public void accept(IVisitor visitor) { visitor.visit(this); } } |
It doesn't really matters if the polymorphic methods with the same name but different signatures are used or not, because in either way the type is known at compile time sot for each new visitable object this method must be implemented accordingly. The main advantage of the fact that new visitors can be easily added is compensated by the fact that the addition of new visitable objects is really hard.
Visitor Pattern using Reflection
public abstract class Visitor { public abstract void visit(Customer customer); public abstract void visit(Order order); public abstract void visit(Item item); public abstract void defaultVisit(Object object); public void visit(Object object) { try { Method downPolymorphic = object.getClass().getMethod("visit", new Class[] { object.getClass() }); if (downPolymorphic == null) { defaultVisit(object); } else { downPolymorphic.invoke(this, new Object[] {object}); } } catch (NoSuchMethodException e) { this.defaultVisit(object); } catch (InvocationTargetException e) { this.defaultVisit(object); } catch (IllegalAccessException e) { this.defaultVisit(object); } } } |
Another point that should be marked is the defaultVisit method: We should visit only classes we know:
public void defaultVisit(Object object) { // if we don't know the class we do nothing if (object.getClass().equals(Product.class)) { System.out.println("default visit: " + object.getClass().getSimpleName()); itemsNo++; } } |
The iterator pattern and visitor pattern has the same benefit, they are used to traverse object structures. The main difference is that the iterator is intended to be used on collections. Usually collections contain objects of the same type. The visitor pattern can be used on complex structure such as hierarchical structures or composite structures. In this case the accept method of a complex object should call the accept method of all the child objects.
Another difference is operation performed on the objects: In one case the visitor defines the operations that should be performed, while the iterator is used by the client to iterate through the objects form a collection and the operations are defined by the client itself.
- The visitor pattern is a great way to provide a flexible design for adding new visitors to extend existing functionality without changing existing code
- The Visitor pattern comes with a drawback: If a new visitable object is added to the framework structure all the implemented visitors need to be modified. The separation of visitors and visitable is only in one sense: visitors depend of visitable objects while visitable are not dependent of visitors.
- Part of the dependency problems can be solved by using reflection with a performance cost.
The arguments and return types for the visiting methods needs to be known in advance, so the Visitor pattern is not good for situtations where these visited classes are subject to change. Every time a new type of Element is added, every Visitor derived class must be amended.
http://butunclebob.com/ArticleS.UncleBob.IuseVisitorI 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 Principlebecause 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
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
问题的核心就是Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,而不知道对象的真实类型;而方法的调用则是根据对象的真实类型,而不是静态类型。
访问者模式的优点
● 好的扩展性
能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
● 好的复用性
可以通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
● 分离无关行为
可以通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
访问者模式的缺点
● 对象结构变化很困难
不适用于对象结构中的类经常变化的情况,因为对象结构发生了改变,访问者的接口和访问者的实现都要发生相应的改变,代价太高。
● 破坏封装
访问者模式通常需要对象结构开放内部数据给访问者和ObjectStructrue,这破坏了对象的封装性。