What
訪問者模式(Visitor Pattern),允許一個或者多個操作應(yīng)用到一組對象上,解耦操作和對象本身。我們使用了一個訪問者類,它改變了元素類的執(zhí)行算法。通過這種方式,元素的執(zhí)行算法可以隨著訪問者改變而改變。這種類型的設(shè)計模式屬于行為型模式。根據(jù)模式,元素對象已接受訪問者對象,這樣訪問者對象就可以處理元素對象上的操作。
Why
- 符合單一職責(zé)原則。 訪問者模式將操作和對象解耦,每個類的職責(zé)非常單一。
- 優(yōu)秀的擴(kuò)展性。 如果想增加操作類型,無須對已有的穩(wěn)定的對象類進(jìn)行更改,具有很好的擴(kuò)展性。
- 靈活性。優(yōu)秀擴(kuò)展性的必然結(jié)果,使得增加或刪除操作類型都很方便。
When
- 對象結(jié)構(gòu)中對象對應(yīng)的類很少改變,但經(jīng)常需要在此對象結(jié)構(gòu)上定義新的操作。
- 需要對一個對象結(jié)構(gòu)中的對象進(jìn)行很多不同的并且不相關(guān)的操作,而需要避免讓這些操作"污染"這些對象的類,也不希望在增加新操作時修改這些類。
How
訪問者模式的實(shí)現(xiàn)是比較難理解的,但訪問者的實(shí)現(xiàn)大家不必掌握,只需要見到能認(rèn)出是訪問者模式就好了。
下面,我們以實(shí)驗(yàn)室年底考核產(chǎn)生報表為例介紹訪問者模式的實(shí)現(xiàn)。年底了,你的實(shí)驗(yàn)室迎來了一年一度的考核,實(shí)驗(yàn)室參與年終考核的有學(xué)碩和專碩,考核官包括導(dǎo)師和輔導(dǎo)員。對于導(dǎo)師來說,他關(guān)注的是學(xué)生的科研或者項(xiàng)目情況,而輔導(dǎo)員則更關(guān)注學(xué)生的課程成績和社會實(shí)踐。在這個例子中,學(xué)碩和專碩就是對象;而學(xué)生的科研、項(xiàng)目情況就是操作,而訪問者的類型包括導(dǎo)師和輔導(dǎo)員。
UML圖如下所示:

包含以下幾部分:
- Visitor:接口或者抽象類,定義了對每個 Master子類 訪問的行為,它的參數(shù)就是被訪問的元素,它的方法個數(shù)理論上與元素的個數(shù)是一樣的,因此,訪問者模式要求元素的類型要穩(wěn)定,如果經(jīng)常添加、移除元素類,必然會導(dǎo)致頻繁地修改 Visitor 接口,如果出現(xiàn)這種情況,則說明不適合使用訪問者模式。
- ConcreteVisitor:具體的訪問者,如本例中的MentorVisitor,它需要給出對每一個元素類訪問時所產(chǎn)生的具體行為。
- Master:元素接口或者抽象類,它定義了一個接受訪問者(accept)的方法,其意義是指每一個元素都要可以被訪問者訪問。
- AcademicMaster、EngineerMaster:具體的元素類,它提供接受訪問的具體實(shí)現(xiàn),而這個具體的實(shí)現(xiàn),通常情況下是使用訪問者提供的訪問該元素類的方法。
具體代碼如下:
首先是Master及其子類。
// 碩士抽象類,定義基本屬性,并定義accept方法,參數(shù)為 Vistor 對象。
public abstract class Master {
private String name;
private Double gpa;
private Double socialPractice;
private Integer paperCount;
private Integer projectCount;
public Master(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
this.name = name;
this.gpa = gpa;
this.socialPractice = socialPractice;
this.paperCount = paperCount;
this.projectCount = projectCount;
}
// 省略getter方法
public abstract void accept(Visitor visitor);
}
// 學(xué)碩類
public class AcademicMaster extends Master {
public AcademicMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
super(name, gpa, socialPractice, paperCount, projectCount);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 專碩類
public class EngineerMaster extends Master {
public EngineerMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
super(name, gpa, socialPractice, paperCount, projectCount);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
接下來是Visitor接口及其實(shí)現(xiàn)類。
public interface Visitor {
void visit(AcademicMaster academicMaster);
void visit(EngineerMaster engineerMaster);
}
// 導(dǎo)師報表
public class MentorVisitor implements Visitor {
@Override
public void visit(AcademicMaster academicMaster) {
System.out.println(String.format("name: %s, paper count: %d",
academicMaster.getName(), academicMaster.getPaperCount()));
}
@Override
public void visit(EngineerMaster engineerMaster) {
System.out.println(String.format("name: %s, project count: %d",
engineerMaster.getName(), engineerMaster.getProjectCount()));
}
}
// 輔導(dǎo)員報表
public class CounselorVisitor implements Visitor {
@Override
public void visit(AcademicMaster academicMaster) {
System.out.println(String.format("name: %s, gpa: %f",
academicMaster.getName(), academicMaster.getGpa()));
}
@Override
public void visit(EngineerMaster engineerMaster) {
System.out.println(String.format("name: %s, social practice: %f",
engineerMaster.getName(), engineerMaster.getSocialPractice()));
}
}
通過上面兩部分代碼,大家可以看出,對Master類對象的操作都集中在Visitor實(shí)現(xiàn)中,這就實(shí)現(xiàn)了對象元素和操作解耦。如果增加訪問者,如家長或者院領(lǐng)導(dǎo)都無須修改Master類,只需要增加相應(yīng)的訪問者實(shí)現(xiàn)就可以了。
最后,給個測試類。
public class TestMain {
public static void main(String[] args) {
Master academicMaster1 = new AcademicMaster("Jeremy", 3.7, 3.0, 1, 3);
Master academicMaster2 = new AcademicMaster("Bob", 3.2, 2.0, 2, 1);
Master engineerMaster1 = new EngineerMaster("Tom", 3.3, 4.0, 0, 1);
Master engineerMaster2 = new EngineerMaster("Amy", 3.0, 3.0, 1, 2);
List<Master> masters = new ArrayList<>();
masters.add(academicMaster1);
masters.add(academicMaster2);
masters.add(engineerMaster1);
masters.add(engineerMaster2);
System.out.println("-----mentor's report-----");
Visitor mentorVisitor = new MentorVisitor();
for (Master master : masters) {
master.accept(mentorVisitor);
}
System.out.println("-----counselor's report-----");
Visitor counselorVisitor = new CounselorVisitor();
for (Master master : masters) {
master.accept(counselorVisitor);
}
}
}
輸出如下:
-----mentor's report-----
name: Jeremy, paper count: 1
name: Bob, paper count: 2
name: Tom, project count: 1
name: Amy, project count: 2
-----counselor's report-----
name: Jeremy, gpa: 3.700000
name: Bob, gpa: 3.200000
name: Tom, social practice: 4.000000
name: Amy, social practice: 3.000000
搞定。
代碼地址
寫在最后
如果你覺得我寫的文章幫到了你,歡迎點(diǎn)贊、評論、分享、贊賞哦,你們的鼓勵是我不斷創(chuàng)作的動力~