visitor-acceptor訪問者設計模式筆記
宣告: 這種模式產生的原因是主要解決:穩定的資料結構和易變的操作耦合問題。
在Java中, 一個鮮明的例子: 大家都知道, Collection好象是個黑色大染缸, 本來有各種鮮明型別特徵的物件一旦放入後, 再取出時, 這些型別資訊就消失了(僅僅知道父型別而無法確定具體的子型別). 那麼我們勢必要用If-Else或Switch這種提交結合instanceof來判斷.
訪問者模式適用於資料結構(資料結構多采用Composite模式封裝)相對穩定的系統,它把資料結構和作用於資料結構上的操作之間的耦合解脫開,使得操作集合可以相對自由地演化。訪問者模式實際上是分離了集合中的元素和對這些元素進行的操作。不過使用訪問者模式是有前提的, 儘量確保Visitor很少變化, 當資料結構變化時,他的visitor介面及其實現都要改變.
抽象訪問者(Visitor)角色: 定義介面, 宣告一個或多個訪問操作.
具體訪問者(ConcreteVisitor)角色: 實現抽象訪問者所宣告的介面, 也就是抽象訪問者所宣告的各個訪問操作. 類似一個回撥委託.
抽象被訪問者(Acceptor)角色: 宣告一個接受操作, 接受一個訪問者物件作為一個引數.
具體被訪問者(ConcreteAcceptor)角色: 實現抽象被訪問者所規定的接受操作. 一般是容器集合類的元素節點.
物件結構(ObjectStructure)角色: 可以遍歷結構中的所有元素, 提供一個介面讓訪問者物件都可以訪問每一個元素.
例子1:
public interface Acceptor {
void accept(Visitor visitor);
}
public interface Visitor {
void visit(String str);
void visit(int i);
void visit(BigInteger integer);
}
public class Strings implements Acceptor {
private String value;
public Strings(String value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(value);
}
}
public class Integers implements Acceptor {
private int value;
public Integers(int value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(value);
}
}
public class BigIntegers implements Acceptor {
private BigInteger value;
public BigIntegers(BigInteger value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(value);
}
}
public class DefaultVisitor implements Visitor {
@Override
public void visit(String str) {
System.out.println("String -> " + str);
}
@Override
public void visit(int i) {
System.out.println("int -> " + i);
}
@Override
public void visit(BigInteger integer) {
System.out.println("BigInteger -> " + integer.toString());
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Acceptor> list = new ArrayList<>();
list.add(new Strings("66666"));
list.add(new Integers(3234));
list.add(new BigIntegers(new BigInteger("232342324")));
DefaultVisitor visitor = new DefaultVisitor();
for (Acceptor acceptor : list) {
acceptor.accept(visitor);
}
}
}
例子2:
public interface Acceptor {
void accept(Visitor visitor);
String getType();
}
public interface Visitor {
void visit(Strings str);
void visit(Integers i);
void visit(BigIntegers integer);
}
public class Strings implements Acceptor {
private String value;
public Strings(String value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getType() {
return "Strings";
}
public String getString() {
return value;
}
}
public class Integers implements Acceptor {
private int value;
public Integers(int value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getType() {
return "Integers";
}
public int getInteger() {
return value;
}
}
public class BigIntegers implements Acceptor {
private BigInteger value;
public BigIntegers(BigInteger value) {
this.value = value;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String getType() {
return "BigIntegers";
}
public BigInteger getBigInteger() {
return value;
}
}
public class VisitorAdapter implements Visitor {
@Override
public void visit(Strings str) {
// no-op
}
@Override
public void visit(Integers i) {
// empty-implementation
}
@Override
public void visit(BigIntegers integer) {
// do nothing
}
}
public class DefaultVisitor extends VisitorAdapter {
@Override
public void visit(Strings str) {
System.out.println(str.getType() + " -> " + str.getString());
}
@Override
public void visit(Integers i) {
System.out.println(i.getType() + " -> " + i.getInteger());
}
}
public class BigIntegerVisitor extends VisitorAdapter {
@Override
public void visit(BigIntegers i) {
System.out.println(i.getType() + " -> " + i.getBigInteger());
}
}
public class ObjectStructure {
private final List<Acceptor> list = new ArrayList<>();
public void add(Acceptor acceptor) {
list.add(acceptor);
}
public void remove(Acceptor acceptor) {
list.remove(acceptor);
}
public void visitAll(Visitor visitor) {
for (Acceptor acceptor : list) {
acceptor.accept(visitor);
}
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.add(new Strings("66666"));
objectStructure.add(new Integers(3234));
objectStructure.add(new BigIntegers(new BigInteger("232342324")));
System.out.println("DefaultVisitor-----------------------");
Visitor visitor = new DefaultVisitor();
objectStructure.visitAll(visitor);
System.out.println("BigIntegerVisitor-----------------------");
visitor = new BigIntegerVisitor();
objectStructure.visitAll(visitor);
}
}
仔細比對例1和例2, 通過VisitorAdapter, 能適度減弱訪問者的缺陷, 如果沒有它, 增加一個被訪問者節點, 將影響所有具體訪問者.
引用陳詞(http://www.cnblogs.com/zhenyulu/articles/79719.html)
面向物件的設計原則中最重要的便是所謂的”開一閉”原則。一個軟體系統的設計應當儘量做到對擴充套件開放,對修改關閉。達到這個原則的途徑就是遵循”對變化的封裝”的原則。
很多系統可以按照演算法和資料結構分開,也就是說一些物件含有演算法,而另一些物件含有資料,接受演算法的操作。如果這樣的系統有比較穩定的資料結構,又有易於變化的演算法的話,使用訪問者模式就是比較合適的,因為訪問者模式使得演算法操作的增加變得容易。
反過來,如果這樣一個系統的資料結構物件易於變化,經常要有新的資料物件增加進來的話,就不適合使用訪問者模式。因為在訪問者模式中增加新的節點很困難,要涉及到在抽象訪問者和所有的具體訪問者中增加新的方法。
之所以備註這篇筆記, 是在研究ANTLR的時候, 突然看明白了visitor模式, 然後我上網查考了很多這方面的部落格(已經不記得有多少了), 將有感觸的文字拷貝了過來. 除了兩個例子, 無原創, 也不指著這個盈利或出名, 如果作者不幸看到, 恰巧又不太高興, 請直接找我吧, 感謝您的文字!