The Visitor Pattern
08/05/2023 1
The Problem
08/05/2023 2
The Problem
Show me everything in a consistent manner …
… and send it to my friend via email
oh, and send it to her phone as well
and print it …
08/05/2023 3
Some analysis music please...
AmazoneNote GoogleNote NewsNote RssNote
title heading heading heading
author excerpt byLine text
isbn url excerpt date
price
08/05/2023 4
Commonality is in the eye of the beholder
Note
AmazoneNote GoogleNote NewsNote RssNote
title heading heading heading
author excerpt byLine text
isbn url excerpt date
price
08/05/2023 5
Need do operations in Note structure
• With a little casual application of polymorphism...
Note
printHeading()
printDetails()
asPrintable() Does this look bad to you?
asEmailContent()
asSMS()
…
AmazoneNote GoogleNote NewsNote RssNote
08/05/2023 6
The solution
Visitor Pattern
• "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"
GOF
08/05/2023 7
Structure
Visitor
Client
visitConcreteElement1(e : ConcreteElement1)
visitConcreteElement2(e : ConcreteElement2)
ConcreteVisistor1 ConcreteVisistor2
visitConcreteElement1(e : ConcreteElement1) visitConcreteElement1(e : ConcreteElement1)
visitConcreteElement2(e : ConcreteElement2) visitConcreteElement2(e : ConcreteElement2)
Element
ObjectStructure
accept(v : Visitor)
ConcreteElement1 ConcreteElement2
visitor.visitConcreteElement1(this); accept(visitor : Visitor) accept(visitor : Visitor)
08/05/2023 8
A solution to a problem in a context
Note
accept(v : NodeVisitor)
AmazoneNote GoogleNote NewsNote RssNote
<<Interface>>
NoteVisitor
+ visitAmazoneNote()
+ visitGoogleNote()
+ visitNewsNote()
+ visitRssNote()
NoteHeadingPrinter NoteDetailsPrinter
08/05/2023 9
Let’s show it in code!
public interface Note {
void accept(NoteVisitor v);
}
public interface NoteVisitor {
void visitAmazoneNote(AmazoneNote note);
void visitGoogleNote(GoogleNote note);
void visitNewsNote(NewsNote note);
void visitRssNote(RssNote note);
}
08/05/2023 10
AmazoneNote
public class AmazoneNote implements Note {
String title;
String author;
String isbn;
double price;
public AmazoneNote(String title, String author,
String isbn, double price) {
this.title = title;
this.author = author;
this.isbn = isbn;
this.price = price;
}
public void accept(NoteVisitor visitor) {
visitor.visitAmazoneNote(this);
}
}
08/05/2023 11
GoogleNote
public class GoogleNote implements Note {
String heading;
String excerpt;
String url;
public GoogleNote(String heading, String excerpt,
String url) {
this.heading = heading;
this.excerpt = excerpt;
this.url = url;
}
public void accept(NoteVisitor visitor) {
visitor.visitGoogleNote(this);
}
}
08/05/2023 12
NewsNote
public class NewsNote implements Note {
String heading;
String byLine;
String excerpt;
public NewsNote(String heading, String byLine,
String excerpt) {
this.heading = heading;
this.byLine = byLine;
this.excerpt = excerpt;
}
public void accept(NoteVisitor visitor) {
visitor.visitNewsNote(this);
}
}
08/05/2023 13
RssNote
public class RssNote implements Note {
String heading;
String text;
Date date;
public RssNote(String heading, String text) {
this.heading = heading;
this.text = text;
this.date = new Date(System.currentTimeMillis());
}
public void accept(NoteVisitor visitor) {
visitor.visitRssNote(this);
}
}
08/05/2023 14
NoteHeadingPrinter visitor
public class NoteHeadingPrinter implements NoteVisitor {
public void visitAmazoneNote(AmazoneNote note) {
System.out.println(note.title);
}
public void visitGoogleNote(GoogleNote note) {
System.out.println(note.heading);
}
public void visitNewsNote(NewsNote note) {
System.out.println(note.heading);
}
public void visitRssNote(RssNote note) {
System.out.println(note.heading);
}
}
08/05/2023 15
NoteDetailsPrinter visitor
public class NoteDetailsPrinter implements NoteVisitor {
public void visitAmazoneNote(AmazoneNote note) {
System.out.println(note.title);
System.out.println(note.price);
}
public void visitGoogleNote(GoogleNote note) {
System.out.println(note.heading);
System.out.println(note.excerpt);
}
public void visitNewsNote(NewsNote note) {
System.out.println(note.heading);
System.out.println(note.byLine);
System.out.println(note.excerpt);
}
public void visitRssNote(RssNote note) {
System.out.println(note.heading);
System.out.println(note.text);
}
}
08/05/2023 16
NoteDetailsPrinter visitor
public class NoteDetailsPrinter implements NoteVisitor {
public void visitAmazoneNote(AmazoneNote note) {
System.out.println(note.title);
System.out.println(note.price);
}
public void visitGoogleNote(GoogleNote note) {
System.out.println(note.heading);
System.out.println(note.excerpt);
}
public void visitNewsNote(NewsNote note) {
System.out.println(note.heading);
System.out.println(note.byLine);
System.out.println(note.excerpt);
}
public void visitRssNote(RssNote note) {
System.out.println(note.heading);
System.out.println(note.text);
}
}
08/05/2023 17
NoteIterator
public class NoteIterator {
private List<Note> sampleNotes() {
List<Note> notes = new ArrayList<Note>();
notes.add(new NewsNote("Melbourne Pushes Boundaries", "Tim Colebatch",
"Melbourne is experiencing its biggest growth surge since 1960's"));
notes.add(new GoogleNote(
"Redhill Consulting Pty Ltd - Ruby on Rails plugins",
"Here are some Ruby on Rails plugins we're developed ...",
"http://www.redhillconsulting.com.au/rails_plugins.html"));
notes.add(new AmazoneNote("The Wolves in the Wall", "Nail Gaiman",
"ISBN XXX-XXXX-XXXXX", 12.06));
notes.add(new RssNote("let's talk about tests, baby...",
"Some long babbling about test naming heuristics"));
return notes;
}
public void visitAllUsing(NoteVisitor visitor) {
for (Note note : sampleNotes()) {
note.accept(visitor);
System.out.println();
}
}
}08/05/2023 18
Test Drive
public class NoteTestDrive {
public static void main(String[] args) {
NoteIterator iterator = new NoteIterator();
iterator.visitAllUsing(new NoteHeadingPrinter());
iterator.visitAllUsing(new NoteDetailsPrinter());
}
}
08/05/2023 19
Output
Melbourne Pushes Boundaries
The Wolves in the Wall Output of NoteHeadingPrinter
Redhill Consulting Pty Ltd - Ruby on Rails Plugins
Let's talk about tests, baby...
Melbourne Pushes Boundaries
Tim Colebatch
Melbourne is experiencing its biggest growth surge since the 1960's
The Wolves in the Wall
12.06
Output of NoteDetailsPrinter
Redhill Consulting Pty Ltd - Ruby on Rails Plugins
Here are some Ruby on Rails plugins we've developed...
Let's talk about tests, baby...
Some long babbling about test naming heuristics
08/05/2023 20
When?
• “An object structure contains many classes of objects
with differing interfaces, and you want to perform
operations on these objects that depend on their
concrete classes”
• “Many distinct and unrelated operations need to be
performed on objects in an object structure, and you
want to avoid ‘polluting’ their classes with these
operations. Visitor lets you keep unrelated operations
together by defining them in one class. When the object
structure is shared by many applications, use Visitor to
put operations in just those applications that need them”
08/05/2023 21
When?
• “The classes defining the object structure rarely
change, but you often want to define new
operations over the structure. Changing the
object structure classes requires redefining the
interface to all visitors, which is potentially costly.
If the object structure classes change often, then
it’s probably better to define the operations in
those classes”
08/05/2023 22
Consequences
• Visitor makes adding new operations easy
• A visitor gathers related operations and
separates unrelated ones
• Adding new ConcreteElement classes is hard
• Visitors can cross object hierarchies
• Visitors can accumulate state
• Visitors may compromise encapsulation
08/05/2023 23
File Folder Examples
FileSystemElement
name : String Visitor
getName() visitFile(file : File)
getElement(name) visitFolder(folder : Folder)
createIterator()
accept(v : Vistor)
PrintVisitor CountFileVisitor
visitFile(file : File) visitFile(file : File)
visitFolder(folder : Folder) visitFolder(folder : Folder)
File Folder
size : int children : ArrayList
getElement() addElement(FileSystemElement)
createIterator() removeElement(FileSystemElement)
accept() getElement(name)
createIterator()
accept(v : Visitor)
08/05/2023 24
Shape Example
08/05/2023 25
We are going to build a Car!
• The car is a class that comprises car elements
of 3 different types :
– Engine (name, price)
– Wheel (name, price)
– Body (name, price)
• Tasks: calculate car fabrication cost, time and
print the list of required parts
• In order to perform those tasks, we need to
implement the following operations for each one
of the parts:
– 1. Fabrication cost calculation
– 2. Fabrication time calculation
– 3. Print information about the part
08/05/2023 26
Design without the Visitor
08/05/2023 27
Shopping cart
• For example, think of a Shopping cart where we
can add different type of items (Elements as
Book (price and isbnNumbers) and Fruit (price,
weight and name). When we click on checkout
button, it calculates the total amount to be paid.
• Now we calculate the total amount to be paid
and print the list of buy elements
• In order to perform those tasks, we need to
implement the following operations for each one
of the Elements:
– 1. Fabrication cost calculation
– 2. Print information about the list of buy element
08/05/2023 29
Graph
• Cho một đồ thị vô hướng G, gồm n đỉnh,
• Để thăm tất cả các đỉnh của đồ thị ta thực hiện
theo 2 phương pháp:
– Thăm theo chiều rộng
– Thăm theo chiều sâu
• Yêu cầu: Thực hiện các thao tác in các đỉnh đã
duyệt và lưu các đỉnh đã duyệt
• Yêu cầu: áp dụng mẫu thiết kế Visitor để vẽ sơ
đồ UML giải quyết bài toán về Đồ thị.
08/05/2023 31