假設有男人和女人兩種元素,要分别列印出他們在不同狀态時的不同表現。
用OO的思想把表現(行為)提取出來作為一個抽象方法,代碼如下:
用if-else對狀态進行判斷
Person接口
public interface Person {
public void action(String state);
}
Man實作類
Java代碼
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
public class Man implements Person{
public void action(String state) {
if(state == "success"){
System.out.println("當男人成功時,背後多半有一個偉大的女人");
}
else if(state == "love"){
System.out.println("當男人戀愛時,凡事不懂也裝懂");
}
}
}
Woman實作類
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
public class Woman implements Person{
public void action(String state) {
if(state == "success"){
System.out.println("當女人成功時,背後大多有一個不成功的男人");
}
else if(state == "love"){
System.out.println("當女人戀愛時,遇事懂也裝不懂");
}
}
}
用戶端測試代碼
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
public class Client {
public static void main(String[] args) {
Person man = new Man();
Person woman = new Woman();
man.action("success");
woman.action("success");
man.action("love");
woman.action("love");
}
}
結果顯示:
當男人成功時,背後多半有一個偉大的女人
當女人成功時,背後大多有一個不成功的男人
當男人戀愛時,凡事不懂也裝懂
當女人戀愛時,遇事懂也裝不懂
當需求發生變化時,要增加一種失敗狀态時,增加男人女人的不同表現,這時就要修改Man類與Woman類的if else,違反了ocp原則(對增加開放-對修改封閉)。而且随着需求的增加,Man類與Woman類的if,else越來越臃腫,需要取消時,又要去修改if,else,既不友善,又容易出錯。
這時候通路者模式可以派上用場了。
請看下面經修改後的類圖:
使用通路者模式
把狀态抽象出來成為一個接口(通路者),不同的狀态就作為狀态的不同實作類(不同的通路者)。
狀态的接口(通路者接口)
public interface Visitor {
public void visit(Man man);
public void visit(Woman woman);
}
具體通路者實作類(分别代表不同的狀态)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
//成功時Man與Woman的不同表現
public class Success implements Visitor{
public void visit(Man man) {
System.out.println("當男人成功時,背後多半有一個偉大的女人");
}
public void visit(Woman woman) {
System.out.println("當女人成功時,背後大多有一個不成功的男人");
}
}
public class Love implements Visitor{
public void visit(Man man) {
System.out.println("當男人戀愛時,凡事不懂也裝懂");
}
public void visit(Woman woman) {
System.out.println("當女人戀愛時,遇事懂也裝不懂");
}
}
按照類圖改造一下人的接口與實作類
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
public interface Person {
void accept(Visitor visitor);
}
public class Man implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Woman implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
這時Man與Woman變得輕盈多了,不再需要寫一大段的if,else,隻需要按不同的狀态,傳入不同的通路者,執行通路者的方法就OK了。
為了更好地實作客戶類與具體元素的解耦,加入一個ObjectStructure類。有了ObjectStructure能更友善地執行一些任何,其具體細節對于用戶端來說是透明的。
import java.util.*;
public class ObjectStructure {
private List<Person> elements = new ArrayList<Person>();
public void attach(Person element){
elements.add(element);
}
public void detach(Person element){
elements.remove(elements);
}
//周遊各種具體元素并執行他們的accept方法
public void display(Visitor visitor){
for(Person p:elements){
p.accept(visitor);
}
}
}
用戶端測試代碼:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依賴于ObjectStructure
//執行個體化具體元素
o.attach(new Man());
o.attach(new Woman());
//當成功時不同元素的不同反映
Visitor success = new Success(); //依賴于抽象的Visitor接口
o.display(success);
//當戀愛時的不同反映
Visitor amativeness = new Love(); //依賴于抽象的Visitor接口
o.display(amativeness);
}
}
這時用戶端隻依賴于ObjectStructure類與Visitor接口,實作了高度的解耦,具體Visitor實作類的執行個體化與具體元素(Man,Woman)的建立可以通過工廠模式甚至配合properties/xml配置檔案建立,無需用戶端關注。而Man與Gril的建立的代碼也可以放在其他地方,用戶端無需知道,如可以放到ObjectStructure的構造函數中完成
public ObjectStructure(){
attach(new Man());
attach(new Woman());
}
在執行個體塊{ }中完成執行個體化也可以。這時如果要增加一種狀态的不同操作,隻需要增加一個具體通路者,無需要修改具體元素類Man與Woman。代碼如下,
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
public class Fail implements Visitor{
public void visit(Man man) {
System.out.println("當男人失敗時,悶頭喝酒,誰也不用勸");
}
public void visit(Woman woman) {
System.out.println("當女人失敗時,眼淚汪汪,誰也勸不了");
}
}
修改一下用戶端測試代碼就OK:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM1QzNlZzN1MmYxUmY5IDZwQGOiFWNkZjZ4IjMyE2Yh9CXyEzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依賴于ObjectStructure
//執行個體化具體元素
o.attach(new Man());
o.attach(new Woman());
//當成功時不同元素的不同反映
Visitor success = new Success(); //依賴于抽象的Visitor接口
o.display(success);
//當戀愛時的不同反映
Visitor amativeness = new Love(); //依賴于抽象的Visitor接口
o.display(amativeness);
//當失敗時的不同反映
Visitor fail = new Fail();
o.display(fail);
}
}
當男人失敗時,悶頭喝酒,誰也不用勸
當女人失敗時,眼淚汪汪,誰也勸不了
現在來讓我們看看通路者模式的定義與類圖:
通路者模式定義:表示一個作用于某個對象結構中的各元素的操作。它使可以在不改變各元素的類的前提下定義作用于這些元素的新操作。
通路者模式的特點:
1)優點:使用了通路者模式以後,對于原來的類層次增加新的操作,僅僅需要實作一個具體通路者角色就可以了,而不必修改整個類層次,使得類層次結構的代碼臃腫難以維護。而且這樣符合“開閉原則”的要求。而且每個具體的通路者角色都對應于一個相關操作,是以如果一個操作的需求有變,那麼僅僅修改一個具體通路者角色,而不用改動整個類層次。
2)通路者模式的雙重分派技術
(1)将具體通路者作為參數傳遞給具體元素角色
(2)進入具體元素角色後,具體元素角色調用者作為參數的具體通路者的visit方法,同時将自己(this)作為參數傳遞進行。具體通路者再根據參數的不同來執行相應的方法
3)前提:開閉原則”的遵循總是片面的。如果系統中的類層次發生了變化,會對通路者模式産生什麼樣的影響呢?你必須修改通路者接口和每一個具體通路者。是以4人組曾經提出,通路者模式适用于資料結構相對穩定的系統。
4)适用情況:通路者模式的目的是要把處理從資料結構分離出來。很多系統可以按照算法和資料結構分開,如果這樣的系統有比較穩定的資料結構,又有易于變化的算法的話,使用通路者模式是比較合适的,因為通路者模式似得算法操作的增加變得容易。反之,如果這樣的系統的資料結構對象易于變化,經常要有新的資料對象增加進來,就不适合使用通路者模式。