What
訪問(wèn)者模式(Visitor Pattern)爆存,允許一個(gè)或者多個(gè)操作應(yīng)用到一組對(duì)象上株扛,解耦操作和對(duì)象本身。我們使用了一個(gè)訪問(wèn)者類(lèi)诉探,它改變了元素類(lèi)的執(zhí)行算法日熬。通過(guò)這種方式,元素的執(zhí)行算法可以隨著訪問(wèn)者改變而改變肾胯。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式竖席。根據(jù)模式,元素對(duì)象已接受訪問(wèn)者對(duì)象敬肚,這樣訪問(wèn)者對(duì)象就可以處理元素對(duì)象上的操作毕荐。
Why
- 符合單一職責(zé)原則。 訪問(wèn)者模式將操作和對(duì)象解耦艳馒,每個(gè)類(lèi)的職責(zé)非常單一憎亚。
- 優(yōu)秀的擴(kuò)展性。 如果想增加操作類(lèi)型鹰溜,無(wú)須對(duì)已有的穩(wěn)定的對(duì)象類(lèi)進(jìn)行更改虽填,具有很好的擴(kuò)展性。
- 靈活性曹动。優(yōu)秀擴(kuò)展性的必然結(jié)果,使得增加或刪除操作類(lèi)型都很方便牲览。
When
- 對(duì)象結(jié)構(gòu)中對(duì)象對(duì)應(yīng)的類(lèi)很少改變墓陈,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作。
- 需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作第献,而需要避免讓這些操作"污染"這些對(duì)象的類(lèi)贡必,也不希望在增加新操作時(shí)修改這些類(lèi)。
How
訪問(wèn)者模式的實(shí)現(xiàn)是比較難理解的庸毫,但訪問(wèn)者的實(shí)現(xiàn)大家不必掌握仔拟,只需要見(jiàn)到能認(rèn)出是訪問(wèn)者模式就好了。
下面飒赃,我們以實(shí)驗(yàn)室年底考核產(chǎn)生報(bào)表為例介紹訪問(wèn)者模式的實(shí)現(xiàn)利花。年底了,你的實(shí)驗(yàn)室迎來(lái)了一年一度的考核载佳,實(shí)驗(yàn)室參與年終考核的有學(xué)碩和專(zhuān)碩炒事,考核官包括導(dǎo)師和輔導(dǎo)員。對(duì)于導(dǎo)師來(lái)說(shuō)蔫慧,他關(guān)注的是學(xué)生的科研或者項(xiàng)目情況挠乳,而輔導(dǎo)員則更關(guān)注學(xué)生的課程成績(jī)和社會(huì)實(shí)踐。在這個(gè)例子中,學(xué)碩和專(zhuān)碩就是對(duì)象睡扬;而學(xué)生的科研盟蚣、項(xiàng)目情況就是操作,而訪問(wèn)者的類(lèi)型包括導(dǎo)師和輔導(dǎo)員卖怜。
UML圖如下所示:
包含以下幾部分:
- Visitor:接口或者抽象類(lèi)刁俭,定義了對(duì)每個(gè) Master子類(lèi) 訪問(wèn)的行為,它的參數(shù)就是被訪問(wèn)的元素韧涨,它的方法個(gè)數(shù)理論上與元素的個(gè)數(shù)是一樣的牍戚,因此,訪問(wèn)者模式要求元素的類(lèi)型要穩(wěn)定虑粥,如果經(jīng)常添加如孝、移除元素類(lèi),必然會(huì)導(dǎo)致頻繁地修改 Visitor 接口娩贷,如果出現(xiàn)這種情況第晰,則說(shuō)明不適合使用訪問(wèn)者模式。
- ConcreteVisitor:具體的訪問(wèn)者彬祖,如本例中的MentorVisitor茁瘦,它需要給出對(duì)每一個(gè)元素類(lèi)訪問(wèn)時(shí)所產(chǎn)生的具體行為。
- Master:元素接口或者抽象類(lèi)储笑,它定義了一個(gè)接受訪問(wèn)者(accept)的方法甜熔,其意義是指每一個(gè)元素都要可以被訪問(wèn)者訪問(wèn)。
- AcademicMaster突倍、EngineerMaster:具體的元素類(lèi)腔稀,它提供接受訪問(wèn)的具體實(shí)現(xiàn),而這個(gè)具體的實(shí)現(xiàn)羽历,通常情況下是使用訪問(wèn)者提供的訪問(wèn)該元素類(lèi)的方法焊虏。
具體代碼如下:
首先是Master及其子類(lèi)。
// 碩士抽象類(lèi)秕磷,定義基本屬性诵闭,并定義accept方法,參數(shù)為 Vistor 對(duì)象澎嚣。
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é)碩類(lèi)
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);
}
}
// 專(zhuān)碩類(lèi)
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);
}
}
接下來(lái)是Visitor接口及其實(shí)現(xiàn)類(lèi)疏尿。
public interface Visitor {
void visit(AcademicMaster academicMaster);
void visit(EngineerMaster engineerMaster);
}
// 導(dǎo)師報(bà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)員報(bà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()));
}
}
通過(guò)上面兩部分代碼,大家可以看出币叹,對(duì)Master類(lèi)對(duì)象的操作都集中在Visitor實(shí)現(xiàn)中润歉,這就實(shí)現(xiàn)了對(duì)象元素和操作解耦。如果增加訪問(wèn)者颈抚,如家長(zhǎng)或者院領(lǐng)導(dǎo)都無(wú)須修改Master類(lèi)踩衩,只需要增加相應(yīng)的訪問(wèn)者實(shí)現(xiàn)就可以了嚼鹉。
最后,給個(gè)測(cè)試類(lèi)驱富。
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
搞定锚赤。
代碼地址
寫(xiě)在最后
如果你覺(jué)得我寫(xiě)的文章幫到了你,歡迎點(diǎn)贊褐鸥、評(píng)論线脚、分享、贊賞哦叫榕,你們的鼓勵(lì)是我不斷創(chuàng)作的動(dòng)力~