一、引子
對於系統中一個已經完成的類層次結構,我們已經給它提供了滿足需求的接口。但是面對新增加的需求,我們應該怎麼做呢?如果這是為數不多的幾次變動,而且你不用為了一個需求的調整而將整個類層次結構統統地修改一遍,那麼直接在原有類層次結構上修改也許是個 不錯 的主意。
但是往往我們遇到的卻是:這樣的需求變動也許會不停的發生;更重要的是需求的任何變動可能都要讓你將整個類層次結構修改個底朝天……。這種類似的操作分布在不同的類裡面,不是一個好現象,我們要對這個結構重構一下了。
那麼,訪問者模式也許是你很好的選擇。
二、定義與結構
訪問者模式,顧名思義使用了這個模式後就可以在不修改已有程序結構的前提下,通過添加額外的“訪問者”來完成對已有代碼功能的提升。
《設計模式》一書對於訪問者模式給出的定義為:表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。從定義可以看出結構對象是使用訪問者模式必須條件,而且這個結構對象必須存在遍歷自身各個對象的方法。這便類似於java中的collection概念了。
以下是訪問者模式的組成結構:
1) 訪問者角色(Visitor):為該對象結構中具體元素角色聲明一個訪問操作接口。該操作接口的名字和參數標識了發送訪問請求給具體訪問者的具體元素角色。這樣訪問者就可以通過該元素角色的特定接口直接訪問它。
2) 具體訪問者角色(Concrete Visitor):實現每個由訪問者角色(Visitor)聲明的操作。
3) 元素角色(Element):定義一個Accept操作,它以一個訪問者為參數。
4) 具體元素角色(Concrete Element):實現由元素角色提供的Accept操作。
5) 對象結構角色(Object Structure):這是使用訪問者模式必備的角色。它要具備以下特征:能枚舉它的元素;可以提供一個高層的接口以允許該訪問者訪問它的元素;可以是一個復合(組合模式)或是一個集合,如一個列表或一個無序集合。
來張類圖就能更加清晰的看清訪問者模式的結構了。
那麼像引言中假想的。我們應該做些什麼才能讓訪問者模式跑起來呢?首先我們要在原有的類層次結構中添加accept方法。然後將這個類層次中的類放到一個對象結構中去。這樣再去創建訪問者角色……
三、舉例
本人閱歷實在可憐,沒能找到訪問者模式在實際應用中的例子。只好借《Thinking in Patterns with java》中的教學代碼一用。我稍微做了下修改。
import java.util.*;
import junit.framework.*;
//訪問者角色
interface Visitor {
void visit(Gladiolus g);
void visit(Runuculus r);
void visit(Chrysanthemum c);
}
// The Flower hierarchy cannot be changed:
//元素角色
interface Flower {
void accept(Visitor v);
}
//以下三個具體元素角色
class Gladiolus implements Flower {
public void accept(Visitor v) { v.visit(this);}
}
class Runuculus implements Flower {
public void accept(Visitor v) { v.visit(this);}
}
class Chrysanthemum implements Flower {
public void accept(Visitor v) { v.visit(this);}
}
// Add the ability to produce a string:
//實現的具體訪問者角色
class StringVal implements Visitor {
String s;
public String toString() { return s; }
public void visit(Gladiolus g) {
s = "Gladiolus";
}
public void visit(Runuculus r) {
s = "Runuculus";
}
public void visit(Chrysanthemum c) {
s = "Chrysanthemum";
}
}
// Add the ability to do "Bee" activities:
//另一個具體訪問者角色
class Bee implements Visitor {
public void visit(Gladiolus g) {
System.out.println("Bee and Gladiolus");
}
public void visit(Runuculus r) {
System.out.println("Bee and Runuculus");
}
public void visit(Chrysanthemum c) {
System.out.println("Bee and Chrysanthemum");
}
}
//這是一個對象生成器
//這不是一個完整的對象結構,這裡僅僅是模擬對象結構中的元素
class FlowerGenerator {
private static Random rand = new Random();
public static Flower newFlower() {
switch (rand.nextInt(3)) {
default:
case 0: return new Gladiolus();
case 1: return new Runuculus();
case 2: return new Chrysanthemum();
}
}
}
//客戶 測試程序
public class BeeAndFlowers extends TestCase {
/*
在這裡你能看到訪問者模式執行的流程:
首先在客戶端先獲得一個具體的訪問者角色
遍歷對象結構
對每一個元素調用accept方法,將具體訪問者角色傳入
這樣就完成了整個過程
*/
//對象結構角色在這裡才 組裝 上
List flowers = new ArrayList();
public BeeAndFlowers() {
for(int i = 0; i < 10; i++)
flowers.add(FlowerGenerator.newFlower());
}
Visitor sval ;
public void test() {
// It’s almost as if I had a function to
// produce a Flower string representation:
//這個地方你可以修改以便使用另外一個具體訪問者角色
sval = new StringVal();
Iterator it = flowers.iterator();
while(it.hasNext()) {
((Flower)it.next()).accept(sval);
System.out.println(sval);
}
}
public static void main(String args[]) {
junit.textui.TestRunner.run(BeeAndFlowers.class);
}
}