Java設計模式(三) Visitor(訪問者)模式及多分派場景應用
阿新 • • 發佈:2017-08-11
public mod 項目 getname total college hide word dsm
基本概念
Visitor
- 封裝一些作用於數據結構中的各元素的操作,不同的操作能夠借助新的visitor實現。減少了操作間的耦合性
- 訪問者能夠將數據結構和對數據的操作解耦,使得添加對數據結構的操作不須要取改動數據結構,也不必去改動原有的操作,而運行時再定義新的Visitor時閑著即可了(在操作加入上易拓展)
模式中角色分工
- Visitor:抽象訪問者,在重載的visit函數中聲明訪問者能夠訪問的對象。
- Concrete Visitor:實現一個訪問者對於一個詳細的元素的操作
- Element:抽象元素,聲明具有訪問該類型元素權限的訪問者的類型(通常是抽象類型)。提供重載的accept函數賦予權限
- Concrete Element:實現accept方法,基本上是模板化的visitor.visit(this)
- Object Structure:容納多種類型也許不同。接口或者不同的元素的集合。
例講Visitor的實現
先是一個簡單的樣例,展現一個最主要的簡陋的Visitor
既然在春招季。我們舉個簡歷篩選的樣例,投簡歷的都是寫本科生、專科生。還有碩士生、高職啊…為了簡單就先取前兩者。求職者的簡歷作為Element實現例如以下:
abstract class Student {
//提供對於數據域基本操作的函數
private String name;
private String university;
private String rating;
//讓指定的visitor獲得操作該對象的權限
public abstract void accept(Visitor visitor);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUniversity() {
return university;
}
public void setUniversity(String university) {
this.university = university;
}
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
}
class Bachelor extends Student{
@Override
public void accept( Visitor visitor ) {
visitor.visit( this );
}
}
class College extends Student{
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
由於我們僅僅定義了兩種學生,所以接口提供了對於兩種Element訪問
interface Visitor{
public void visit ( Bachelor bachelor );
public void visit ( College college );
}
首先篩選簡歷我們看一下大家的簡歷都什麽樣子,那麽須要一個ShowVisitor:
class ShowVisitor implements Visitor {
@Override
public void visit(Bachelor bachelor) {
System.out.println("A bachelor\n");
//TODO 可能會有一些特異的操作,我們為了簡單就省略了
this.printMessage( bachelor );
}
@Override
public void visit(College college) {
System.out.println(" a college student!\n");
//TODO 同上
this.printMessage( college );
}
public void printMessage ( Student student ){
System.out.println( "Name : " + student.getName()+"\n"
+ "University : " + student.getUniversity()+"\n"
+ "Rating : " + student.getRating() + "\n"
);
}
}
要進行測試,我們首先要構造一個數據集合。也就是角色中相應的ObjectStructure,為了簡單我們直接用ArrayList了
public class VisitorEg {
public static void main ( String [] args ){
ArrayList<Student> list = new ArrayList<Student>();
Bachelor bachelor = new Bachelor();
bachelor.setName("llin");
bachelor.setRating("100");
bachelor.setUniversity("Tianjin University");
College college = new College();
college.setUniversity("Tianjin college");
college.setRating("1");
college.setName("dalinge");
list.add ( bachelor );
list.add ( college );
Visitor visitor = new ShowVisitor();
for ( Student student: list ){
student.accept( visitor );
}
}
}
那麽好像看不出訪問者模式有什麽優勢啊!!!並且好費事啊,可是由於你將數據結構和對數據的操作分離了(解耦),所以當我想加入新的操作時,不須要改動原有的類,僅僅須要又一次實現一個visitor就能夠了。
所以,我們回到這個樣例。這麽多人報名,那麽究竟有多少本科生呢(假設人數夠了,可能直接偷懶僅僅面試本科生了),-_-萬惡的這樣的HR,所以我們須要一個統計的Visitor:
class SumVisitor implements Visitor{
private int totalBachelor;
SumVisitor(){
super();
totalBachelor = 0;
}
@Override
public void visit(Bachelor bachelor) {
totalBachelor++;
}
@Override
public void visit(College college) {
}
public int getTotal_bachelor() {
return totalBachelor;
}
}
public class VisitorEg {
public static void main ( String [] args ){
ArrayList<Student> list = new ArrayList<Student>();
Bachelor bachelor = new Bachelor();
bachelor.setName("llin");
bachelor.setRating("100");
bachelor.setUniversity("Tianjin University");
College college = new College();
college.setUniversity("Tianjin college");
college.setRating("1");
college.setName("dalinge");
list.add ( bachelor );
list.add ( college );
Visitor visitor = new ShowVisitor();
Visitor visitor1 = new SumVisitor();
for ( Student student: list ){
student.accept( visitor );
student.accept( visitor1);
}
System.out.println( "The total sum of bachelors : "+ ((SumVisitor)visitor1).getTotal_bachelor() );
}
}
達到了要求。卻沒有改動一行代碼,開心!
Visitor應用場景
一定會有的疑問:visitor和iterator的差別:
- visitor能夠訪問不同的對象(僅僅須要在Element定義相應的accept),可是Iterator僅僅能訪問同樣的對象。最起碼要有同樣的接口
- iterator是不依賴詳細實現的,而visitor是依賴詳細實現的,由於Visitor會依據訪問的詳細的對象來採取相應的操作,而iterator最多僅僅是基於同樣的接口的泛化實現。
- iterator訪問的數據結構的操作和數據並未分離。所以拓展功能起來須要改動,違反了開閉原則和單一職責原則。
可是由於訪問者依賴詳細實現,而不是依賴抽象。所以違反了依賴倒置原則
優缺點決定的應用場景
- 符合單一職責原則。功能上具有良好的拓展性,可是由於依賴詳細實現違背了詳細實現,所以為類的改動帶了麻煩。
- 具有優良的拓展性。僅僅須要實現新的Visitor來滿足新的訪問要求。
由於數據和操作的分離,防止了加入新的操作汙染原來的數據結構。
綜上
訪問者是一種集中規整模式,特別適合用於大規模重構的項目。在這一個階段的需求已經非常清晰,原系統的功能點也已經明白。通過訪問者模式能夠非常easy把一些功能進行梳理,達到終於目的功能集中化
雙分派
首先介紹下面單分派
單分派:一個操作是依據請求者的名稱和接收到的參數決定的,在Java有靜態綁定和動態綁定,各自是通過重載和覆寫實現的。
雙分派:雙分派意味著得到運行的操作決定於請求的種類和接收者的類型。
正相應於訪問者模式。
Javac在構建、優化、解析語法樹的時候就是採用的是Visitor模式(語法、語義分析階段)
Java設計模式(三) Visitor(訪問者)模式及多分派場景應用