在Java中所有的物件都繼承自O(shè)bject物件,這樣作的優(yōu)點(diǎn)之一,就是使得一些集合物件的資料結(jié)構(gòu)容易管理,例如您可以將任何型態(tài)的物件放入Vector中。
然而現(xiàn)在有個(gè)問(wèn)題是,如果您的集合(connection)物件中不僅儲(chǔ)存一種型態(tài)的物件,如果想要對(duì)這些物件作出一些個(gè)別化的操作,首要條件就是要知道該物件的型態(tài),使用 instanceof 似乎是個(gè)不錯(cuò)的方式,在程式簡(jiǎn)單的情況下,也許您會(huì)這麼作:
這麼作並不是不可以,只是將來(lái)的擴(kuò)充性不大,如果今天您想要一次改變對(duì)每一種類型物件的操作,您必須修改很多地方。
從物件自身的角度來(lái)想好了,物件在一個(gè)個(gè)的房子中,物件說(shuō):「不要在房子外費(fèi)盡心思判斷了,即然您不知道我是誰(shuí),那麼您就進(jìn)來(lái)訪問(wèn)我好了,我告訴您我是誰(shuí),這麼一來(lái)您就知道如何操作我了!」
用程式來(lái)實(shí)現(xiàn)上面這個(gè)描述:
Visitor訪問(wèn)是基於overload來(lái)完成,對(duì)於每一個(gè)實(shí)現(xiàn)IElement的物件來(lái)說(shuō),它接受IVisitor來(lái)訪問(wèn)它,在accept()方法中,IVisitor使用正確的方法來(lái)訪問(wèn)IElement(顯然的,這麼部份可以靠不同的函式名稱,或是overload來(lái)達(dá)成),並在visit() 中對(duì)IElement作出對(duì)應(yīng)的操作,如果您今天想要換掉每一個(gè)IElement的操作,只要更換IVisitor類型的物件就可以了,也就是這行:
舉個(gè)實(shí)際的例子,假設(shè)VisitorA只是個(gè)懶惰的推銷員好了,今天有一個(gè)比較勤快的推銷員VisitorB,在訪問(wèn)過(guò)IElement之後,會(huì)對(duì) IElement作出更多的操作,要在程式中實(shí)現(xiàn)VisitorB,只要增加一個(gè)VisitorB類別就可以了:
改一下Main來(lái)示範(fàn):
在範(fàn)例中的System.out.println();只是個(gè)示意,它也可能是您對(duì)IElement的額外方法的直接調(diào)用。
Visitor模式的 UML 結(jié)構(gòu)類圖如下:

在Java World中有一篇文章,提到可以利用reflection來(lái)改進(jìn)使用訪問(wèn)者模式時(shí)的彈性,有興趣的可以進(jìn)一步參考一下Reflect on the Visitor design pattern。
然而現(xiàn)在有個(gè)問(wèn)題是,如果您的集合(connection)物件中不僅儲(chǔ)存一種型態(tài)的物件,如果想要對(duì)這些物件作出一些個(gè)別化的操作,首要條件就是要知道該物件的型態(tài),使用 instanceof 似乎是個(gè)不錯(cuò)的方式,在程式簡(jiǎn)單的情況下,也許您會(huì)這麼作:
public class ElementA {
// some implementing
}
public class ElementB {
// some implementing
}
public class ElementC {
// some implementing
}
// ......
Iterator iterator = arrayList.iterator()
while (iterator.hasNext()) {
if (o instanceof ElementA)
(ElementA) o.operationA();
else if (o instanceof ElementB)
(ElementB) o.operationB();
else if (o instanceof ElementC)
(ElementC) o.operationC();
else
System.out.println(
"Sorry! I don't know who you are! "
+ o.toString());
//....
}
//....
// some implementing
}
public class ElementB {
// some implementing
}
public class ElementC {
// some implementing
}
// ......
Iterator iterator = arrayList.iterator()
while (iterator.hasNext()) {
if (o instanceof ElementA)
(ElementA) o.operationA();
else if (o instanceof ElementB)
(ElementB) o.operationB();
else if (o instanceof ElementC)
(ElementC) o.operationC();
else
System.out.println(
"Sorry! I don't know who you are! "
+ o.toString());
//....
}
//....
這麼作並不是不可以,只是將來(lái)的擴(kuò)充性不大,如果今天您想要一次改變對(duì)每一種類型物件的操作,您必須修改很多地方。
從物件自身的角度來(lái)想好了,物件在一個(gè)個(gè)的房子中,物件說(shuō):「不要在房子外費(fèi)盡心思判斷了,即然您不知道我是誰(shuí),那麼您就進(jìn)來(lái)訪問(wèn)我好了,我告訴您我是誰(shuí),這麼一來(lái)您就知道如何操作我了!」
用程式來(lái)實(shí)現(xiàn)上面這個(gè)描述:
- IElement.java
public interface IElement {
public void accept(IVisitor visitor);
}
- ElementA.java
public class ElementA implements IElement {
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public void operationA() {
System.out.println(
"do A's job....such-and-such....");
}
}
- ElementB.java
public class ElementB implements IElement {
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public void operationB() {
System.out.println(
"do B's job....such-and-such....");
}
}
- ElementC.java
public class ElementC implements IElement {
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public void operationC() {
System.out.println(
"do C's job....such-and-such....");
}
}
- IVisitor.java
public interface IVisitor {
public void visit(ElementA element);
public void visit(ElementB element);
public void visit(ElementC element);
}
- VisitorA.java
public class VisitorA implements IVisitor {
public void visit(ElementA element) {
element.operationA();
}
public void visit(ElementB element) {
element.operationB();
}
public void visit(ElementC element) {
element.operationC();
}
}
- Main.java
public class Main {
public static void main(String[] args) {
// know nothing about their type
// after storing them into Element array
IElement[] list = {new ElementA(),
new ElementB(),
new ElementC()};
IVisitor visitor = new VisitorA();
for (int i=0; i < list.length; i++)
list[i].accept(visitor);
}
}
Visitor訪問(wèn)是基於overload來(lái)完成,對(duì)於每一個(gè)實(shí)現(xiàn)IElement的物件來(lái)說(shuō),它接受IVisitor來(lái)訪問(wèn)它,在accept()方法中,IVisitor使用正確的方法來(lái)訪問(wèn)IElement(顯然的,這麼部份可以靠不同的函式名稱,或是overload來(lái)達(dá)成),並在visit() 中對(duì)IElement作出對(duì)應(yīng)的操作,如果您今天想要換掉每一個(gè)IElement的操作,只要更換IVisitor類型的物件就可以了,也就是這行:
// IVisitor visitor = new VisitorA();
// 換掉一個(gè)IVisitor,就可以換掉所有的操作
// 不用修改多個(gè)地方
IVisitor visitor = new VisitorB();
// 換掉一個(gè)IVisitor,就可以換掉所有的操作
// 不用修改多個(gè)地方
IVisitor visitor = new VisitorB();
舉個(gè)實(shí)際的例子,假設(shè)VisitorA只是個(gè)懶惰的推銷員好了,今天有一個(gè)比較勤快的推銷員VisitorB,在訪問(wèn)過(guò)IElement之後,會(huì)對(duì) IElement作出更多的操作,要在程式中實(shí)現(xiàn)VisitorB,只要增加一個(gè)VisitorB類別就可以了:
- VisitorB.java
public class VisitorB implements IVisitor {
public void visit(ElementA element) {
System.out.println("VisitorB is a hard worker....");
element.operationA();
System.out.println(
"I want to do some extra work on A....");
}
public void visit(ElementB element) {
System.out.println("VisitorB is a hard worker....");
element.operationB();
System.out.println(
"I want to do some extra work on B....");
}
public void visit(ElementC element) {
System.out.println("VisitorB is a hard worker....");
element.operationC();
System.out.println(
"I want to do some extra work on C....");
}
}
改一下Main來(lái)示範(fàn):
- Main.java
public class Main {
public static void main(String[] args) {
IElement[] list = {new ElementA(),
new ElementB(),
new ElementC()};
System.out.println("visitorA is coming.......");
IVisitor visitorA = new VisitorA();
for (int i=0; i < list.length; i++)
list[i].accept(visitorA);
System.out.println("\nvisitorB is coming.......");
IVisitor visitorB = new VisitorB();
for (int i=0; i < list.length; i++)
list[i].accept(visitorB);
}
}
在範(fàn)例中的System.out.println();只是個(gè)示意,它也可能是您對(duì)IElement的額外方法的直接調(diào)用。
Visitor模式的 UML 結(jié)構(gòu)類圖如下:
