訪問者的基本想法是:軟體系統擁有乙個由許多物件構成的、比較穩定的物件結構,這些物件的類都擁有乙個accept方法用來接收對訪問者物件的訪問,而訪問者是乙個介面,他擁有乙個visit方法,這個方法對訪問到的物件結構中不同型別的元素做出不同的處理。在物件結構的一次訪問過程中,我們遍歷整個物件結構,每乙個元素都實施accept方法,而又在每乙個元素的accept方法中會呼叫訪問者模式visit方法,從而是訪問者得以處理物件結構中的每乙個元素,而且能夠對不同的元素分別做不同的處理。說了這麼多,可能有些人還是一頭霧水,說的嘛東西!聽不懂沒關係,你看完了這篇部落格回頭再來體會或許就一目了然了。
舉個例,如果我們有乙個資料結構(list),而這個list中存放的資料(employee)並非一種型別,比如有工程師、銷售、專案經理等,現在集團ceo要給他們發年終獎,而這三類員工的獎金制度都不一樣,所以需要對著幾種型別的員工分別進行相應制度的獎金評審。這個時候就滿足訪問者模式的思想了。到了這裡我們先停一下,就剛剛所訴的案例,如果我們還不會訪問者模式,你此時會如何解決呢?我猜初學者可能會這樣設計,對於裝有不同資料型別的list,我們遍歷的時候會對取出的每乙個型別的員工並進行對應的獎金計算方法呼叫。而計算的方法會在所有員工的父類裡邊宣告:
public class employee
public int calculateprizeforsalesman()
public int calculateprizeformanager()
}
呼叫的時候根據employeetype使用if、else語句來分開評審呼叫。這不很簡單嗎,輕鬆搞定。相信很多人都會這麼做,功能實現了,但是這種寫法會使你的程式非常臃腫,有很大的隱患!如果日後制度改變了,公司整體只提公升了對工程師的獎金評審規則,那怎麼辦?你肯定會重頭檢視**,最後花了很多時間,終於找到了是在employee類的calculateforengineer方法中修改一下**,完工。好的,如果這個時候有乙個大的改變,現在人資需要在這個系統計算每個員工的月薪,你說wtf!這需求為什麼不早說!沒辦法,我只能在這個employee類裡邊再新增四個方法,分別計算不同型別員工的薪資技術操作。假設你以後離職了,現在公司規模擴充,又有新的一類職務(售後服務)作為員工需要新增到獎金評審系統,然後新來的程式設計師就會重頭到尾先梳理一下業務邏輯,然後花了半天最後找到在employee類新增乙個calculateprizeforserver()和calculatesalaryforserver()兩個方法,如果這個程式設計師的能力在你之上,而素質在你之下,那你可能就要時常打一下噴嚏了!這明明能使用訪問者模式來把「資料操作」和「資料結構」分離嘛!這人卻偏偏揉到一起。太tm難維護了,而現在專案已步入成熟穩定的運營階段,想改都改不了了。(而他日後估計改一次,心裡就
從類圖的結構上來說,應該算是比較複雜的了。其實本身也是最難的一種設計模式。
下面介紹一下這五種角色:
抽象元素(employee):乙個抽象類,它定義了接受訪問者(visitor)的操作accept()。
具體元素(engineer):抽象元素的子類,一般有多個,代表不同的資料型別,比如:工程師、經理、銷售員。
抽象訪問者(visitor):乙個介面,定義了操作來自不同資料的所有方法。
具體訪問者(ceovisitor):實現visitor介面的類,實現了針對不同型別的資料進行不同型別的計算。
物件結構(object structure):乙個集合,用於存放element物件(元素),並提供遍歷元素的方法,這裡通常我們使用的就是系統自帶的list集合類。
看了類圖,應該大致明白如何編碼了吧!下面結合**進行說明:
抽象元素:
/**
* 員工的基類 定義了員工的基本工資和接受訪問者的方法
*/public abstract class employee
具體元素:三個職位 基礎工資分別為 一萬、五千、兩萬。
public class engineer extends employee
public int getbasesalary()
}public class salesman extends employee
public int getbasesalary()
}public class manager extends employee
public int getbasesalary()
}
public inte***ce visitor
具體訪問者:ceo評定年終獎、hr評定工資
public class ceovisitor implements visitor
@override
public void visitorengineer(engineer user)
@override
public void visitorsalesman(salesman user)
}public class hrvisitor implements visitor
@override
public void visitorengineer(engineer user)
@override
public void visitorsalesman(salesman user)
}
物件結構:這裡的物件結構我們就用list集合下的arraylist。
下面是測試類:
public class main
system.out.println("-----------");
visitor hrvisitor = new hrvisitor(); //例項化乙個訪問者(hr)
for (employee employee : list)}}
列印出的結果為:
這就完成了乙個簡單的訪問者模式。
現在再去體會文章開頭「訪問者的基本想法」是不是很清楚了呢。每種設計模式自然有他存在的道理,雖然這種模式我們日常開發中並不常見,但你學會了,以後肯定有機會使用到。到那時整個專案層次感就顯得非常清晰,比較訪問者模式的最大優點就是把「資料操作」和「資料結構」分離。如果用傳統的程式設計方式去實現,那**的邏輯會顯得非常複雜,試想一下,乙個物件結構裡有無數個物件,還需要對每一種物件進行對應的資料操作!好繁瑣啊,尤其是針對大專案還交接給別人後,想想改起來的那個感覺。。。
最後我們再總結一下:
一、訪問者模式在不改變類的情況下可有效的增加其上的操作,為了達到這樣的效果,使用了一種稱為「雙重分派」的技術:被訪問者(manger)首先呼叫accept(visitor visitor)方法「接受」訪問者,而被接受的訪問者(ceovisitor)在呼叫visitormanager方法訪問當前的element物件,並對返回的資料進行操作。
二、可以在不改變乙個集合中元素的類情況下,增加新的施加於該元素上的新操作,比如hr對他的實發工資的計算,日後還可以再新增乙個訪問者,用於對員工績效的統計。對元素的操作可以無限增加,而元素本身卻沒有時候的改變。相當於對元素自身的功能操作轉嫁到訪問者中幫它處理了。從整個objectstructure物件結構(包括無數元素物件的集合)中來看,巧妙的實現了「資料操作」和「資料結構」的分離。
三、可以將集合中個元素的某些操作幾種到訪問者中,不僅便於集合的維護,也有利於集合中元素的復用。
使用場景:當乙個物件結構(比如list或它的子類)包含若干個不同種類的元素,針對不同種類的元素需要有不同的操作,且需要遍歷每乙個元素的時候,我們就可以使用到訪問者模式了!
比如上面案例中:
物件結構就是list
不同種類的元素就是經理、工程師和銷售員
不同的操作就是針對三種元素的操作,例如獎金評定或薪資結算
當然前提是需要物件結構需要有迭代器來遍歷它自己的元素。
Java設計模式之訪問者模式
單個單子的介面 相當於element public inte ce bill 消費的單子 public class consumebill implements bill public void accept accountbookviewer viewer public double getamo...
設計模式(JAVA) 訪問者模式
場景 將公司中所有人員資訊都列印匯報出來,其中假設公司有兩類人員 1.普通員工 2.管理層 不同人員型別的資訊是不同的。訪問者模式 定義 封裝一些作用於某種資料結構中的各元素的操作,它可以在不改變資料結構的前提下定義作用於這些元素的新的操作。包含如下幾個角色 1.visitor 抽象訪問者 抽象類或...
Java設計模式 訪問者模式
訪問者模式 visitor pattern 以列印公司員工的資訊報表為例。下面先看下類圖。使用了乙個模版方法模式,把所要的資訊都列印出來。這樣子寫出來的 就不太符合公司不同管理層的需要,不能滿足各自的需求。每個普通員工類和經理類都乙個方法 report,那是否可以把這個方法提取到另外乙個類中來實現呢...