設計模式--訪問者模式Visitor(行為型)

benbenxiongyuan發表於2014-04-15

定義:

1.1 定義:Represent an operation to be performed on the elements of an object structure. Vistor lets you define a new operation without changing the classes of the elements on which it operates.(封裝一些作用於某種資料結構中的各元素的操作,它可以在不改變資料結構的前提下定義作用於這些元素的新的操作。)

1.2 通用類圖:

角色說明:

Visitor——抽象訪問者:

抽象類或者介面,宣告訪問者可以訪問哪些元素,具體到程式中就是visit方法的引數定義哪些物件是可以被訪問的。

ConcreteVisitor——具體訪問者:

它影響訪問者訪問到一個類後該怎麼幹,要做什麼事情。

Element——抽象元素:

介面或者抽象類,宣告接受哪一類訪問者訪問,程式上是通過accept方法中的引數來定義的。

ConcreteElement——具體元素:

實現accept方法,通常是visitor.visit(this),基本上都形成一種模式了。

ObjectStructure——結構物件:

元素產生者,一般容納在多個不同類、不同介面的容器,如ListSetMap等,在專案中,一般很少抽象出這個角色。

一個更易懂的類圖:

1.3 通用程式碼:

注意Vistor.visit(elem)方法,此方法更多的時候是取出elem中成員,按所需處理顯示

public abstract class Element {
	// 定義業務邏輯
	public abstract void doSomething();

	// 允許誰來訪問
	public abstract void accept(IVisitor visitor);
}

public class ConcreteElement1 extends Element {
	// 完善業務邏輯
	public void doSomething() {
		// 業務處理
	}

	// 允許那個訪問者訪問
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}
}

public class ConcreteElement2 extends Element {
	// 完善業務邏輯
	public void doSomething() {
		// 業務處理
	}

	// 允許那個訪問者訪問
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}
}

public interface IVisitor {
	// 可以訪問哪些物件
	public void visit(ConcreteElement1 el1);

	public void visit(ConcreteElement2 el2);
}

public class Visitor implements IVisitor {
	// 訪問el1元素
	public void visit(ConcreteElement1 el1) {
		el1.doSomething();
	}

	// 訪問el2元素
	public void visit(ConcreteElement2 el2) {
		el2.doSomething();
	}
}

public class ObjectStruture {
	// 物件生成器,這裡通過一個工廠方法模式模擬
	public static Element createElement() {
		Random rand = new Random();
		if (rand.nextInt(100) > 50) {
			return new ConcreteElement1();
		} else {
			return new ConcreteElement2();
		}
	}
}

public class Client {
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			// 獲得元素物件
			Element el = ObjectStruture.createElement();
			// 接受訪問者訪問
			el.accept(new Visitor());
		}
	}
}

優點

2.1 符合單一職責原則:具體元素角色負責資料的載入,而Visitor類則負責報表的展現,兩個不同的職責非常明確地分離開來,各自演繹變化;

2.2 優秀的擴充套件性:資料不同的展示,直接在Vistor中增加方法實現;

2.3 靈活性高:對於不同的資料實體,不需要instanceof判斷。

缺點

3.1 具體元素對訪問者公佈細節:違背了LOW原則

3.2 具體元素變更比較困難:增、刪、改都會導致Visitor的修改。

3.3 違背了依賴倒置原則:訪問者依賴的是具體元素,而不是抽象元素,擴充套件比較困難。

應用場景

4.1 一個物件結構包含很多類物件,他們有不同的介面,而你想對這些物件實施一些依賴於其具體類的操作,也就是迭代器模式已經不能勝任的情況。

4.2 需要對一個物件結構中的物件進行很多不同並且不相關的操作,而你想避免這些操作“汙染”這些物件的類。

總結:業務規則要求遍歷多個不同的物件,這本身是訪問者模式的出發點,迭代器模式只能訪問同類或介面的資料(當然了,如果你使用instanceof,那麼能訪問所有的資料),而訪問者是對迭代器模式的擴充,可以遍歷不同的物件,然後針對不同的物件,執行不同的操作。

注意事項

暫無

擴充套件

6.1 統計功能;

6.2 多個訪問者:如顯示、彙總

6.3 雙分派(Java是一個單分派語言:過載時,方法的實參型別源於定義,而方法的所屬物件則是動態繫結。):使用多型+訪問者模式,可以看出java是一個支援雙分派的單分派語言。

原始碼一:測試單分派:

public interface Role {
	// 演員要扮演的角色
}

public class IdiotRole implements Role {
	// 一個弱智角色
}

public class KungFuRole implements Role {
	// 武功天子第一的角色
}

public abstract class AbsActor {
	// 演員都能夠演一個角色
	public void act(Role role) {
		System.out.println("演員可以扮演任何角色");
	}

	// 可以演功夫戲
	public void act(KungFuRole role) {
		System.out.println("演員都可以演功夫角色");
	}
}

public class YoungActor extends AbsActor {
	// 年輕演員最喜歡演功夫戲
	public void act(KungFuRole role) {
		System.out.println("最喜歡演功夫角色");
	}
}

public class OldActor extends AbsActor {
	// 不演功夫角色
	public void act(KungFuRole role) {
		System.out.println("年齡大了,不能演功夫角色");
	}
}

public class Client {
	public static void main(String[] args) {
		// 定義一個演員
		AbsActor actor = new OldActor();
		// 定義一個角色
		Role role = new KungFuRole();
		// 開始演戲
		actor.act(role);
		actor.act(new KungFuRole());
	}
}

原始碼二:實現雙分派:(更改下列程式碼)

public interface Role {
	// 演員要扮演的角色
	public void accept(AbsActor actor);
}

public class KungFuRole implements Role {
	// 武功天子第一的角色
	public void accept(AbsActor actor) {
		actor.act(this);
	}
}

public class IdiotRole implements Role {
	// 一個弱智角色,然誰來扮演
	public void accept(AbsActor actor) {
		actor.act(this);
	}
}

public class Client {
	public static void main(String[] args) {
		// 定義一個演員
		AbsActor actor = new OldActor();
		// 定義一個角色
		Role role = new KungFuRole();
		// 開始演戲
		role.accept(actor);
	}
}

轉自:http://blog.csdn.net/yuanlong_zheng/article/details/7584862

相關文章