Visitor Pattern

Visitor Pattern
http://www.k.hosei.ac.jp/~yukita/
Visitor Pattern の目的
• データ構造と処理の分離
Visitor Pattern
2
Visitor
visit(File)
visit(Directory)
ListVisitor
例題のクラス図
<<interface>>
Acceptor
accept
Entry
currentdir
visit(File)
visit(Directory)
Uses
Main
getName
getSize
add
iterator
File
Directory
name
size
name
dir
accept
getName
getSize
Uses
accept
getName
getSize
add
iterator
Uses
Visitor Pattern
3
Visitor.java
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
Visitor Pattern
4
Acceptor.java
public interface Acceptor {
public abstract void accept(Visitor v);
}
Visitor Pattern
5
Entry.java (1)
import java.util.Iterator;
public abstract class Entry implements Acceptor {
public abstract String getName();
public abstract int getSize();
public Entry add(Entry entry)
throws FileTreatmentException {
throw new FileTreatmentException();
}
Visitor Pattern
6
Entry.java (2)
public Iterator iterator()
throws FileTreatmentException {
throw new FileTreatmentException();
}
public String toString() {
return getName() + " (" + getSize() + ")";
}
}
Visitor Pattern
7
Entry におけるダミー実装
• add, iterator メソッドは File ではエラーとな
る。
• add, iterator メソッドは Directory で意味を
もつ。
Visitor Pattern
8
File.java (1)
public class File extends Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() {
return name;
}
Visitor Pattern
9
File.java (2)
public int getSize() {
return size;
}
public void accept(Visitor v) {
v.visit(this);
}
}
Visitor Pattern
10
Directory.java (1)
import java.util.Iterator;
import java.util.Vector;
public class Directory extends Entry {
private String name;
private Vector dir = new Vector();
public Directory(String name) {
this.name = name;
}
public String getName() {
return name;
}
Visitor Pattern
11
Directory.java (2)
public int getSize() {
int size = 0;
Iterator it = dir.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) {
dir.add(entry);
return this;
Visitor Pattern
}
12
Directory.java (3)
public Iterator iterator() {
return dir.iterator();
}
public void accept(Visitor v) {
v.visit(this);
}
}
Visitor Pattern
13
ListVisitor.java (1)
import java.util.Iterator;
public class ListVisitor extends Visitor {
private String currentdir = "";
public void visit(File file) {
System.out.println(currentdir + "/" + file);
}
Visitor Pattern
14
ListVisitor.java (2)
public void visit(Directory directory) {
System.out.println(currentdir + "/" + directory);
String savedir = currentdir;
currentdir = currentdir + "/" +
directory.getName();
Iterator it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
entry.accept(this);
}
currentdir = savedir;
}
}
Visitor Pattern
15
Visitorが処理を担当
• Visitor インタフェースを実装している
ListVisitor が処理を担当する。
• 一方,Acceptor 側の File, Directory は処
理を担わず,データの保持だけに責任をも
つ。
Visitor Pattern
16
FileTreatmentException.java
public class FileTreatmentException
extends RuntimeException {
public FileTreatmentException() {
}
public FileTreatmentException(String msg) {
super(msg);
}
}
Visitor Pattern
17
Main.java (1)
public class Main {
public static void main(String[] args) {
try {
System.out.println("Making root
entries...");
Directory rootdir = new Directory("root");
Directory bindir = new Directory("bin");
Directory tmpdir = new Directory("tmp");
Directory usrdir = new Directory("usr");
Visitor Pattern
18
Main.java (2)
rootdir.add(bindir);
rootdir.add(tmpdir);
rootdir.add(usrdir);
bindir.add(new File("vi", 10000));
bindir.add(new File("latex", 20000));
rootdir.accept(new ListVisitor());
System.out.println("");
System.out.println("Making user entries...");
Visitor Pattern
19
Main.java (3)
Directory yuki = new Directory("yuki");
Directory hanako = new Directory("hanako");
Directory tomura = new Directory("tomura");
usrdir.add(yuki);
usrdir.add(hanako);
usrdir.add(tomura);
yuki.add(new File("diary.html", 100));
yuki.add(new File("Composite.java", 200));
Visitor Pattern
20
Main.java (4)
hanako.add(new File("memo.tex", 300));
tomura.add(new File("game.doc", 400));
tomura.add(new File("junk.mail", 500));
rootdir.accept(new ListVisitor());
} catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}
Visitor Pattern
21
実行結果
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)
Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)
Visitor Pattern
22
Sequence Diagram
Main
new
:Directory
:ListVisitor
:File
:File
accept
visit
accept
visit
accept
visit
Visitor Pattern
23
パターン
ObjectStructure
Visitor
Acceptor
visit(ConcreteAcceptorA)
visit(ConcreteAcceptorB)
ConcreteVisitor
visit(ConcreteAcceptorA)
visit(ConcreteAcceptorB)
accept
ConcreteAcceptorA
accept
ConcreteAcceptorB
accept
Visitor Pattern
24
注意
• ConcreteVisitor の追加は簡単
– このときConcreteAcceptorの変更は不要
• ConcreteAcceptor の使いは困難
– 例えば Entry のサブクラス Device を追加しよ
うとすれば Visit(Device)メソッドが Visitor側に
必要になる。
Visitor Pattern
25