關(guān)于設(shè)計(jì)模式,我們得結(jié)合生活中的案例來(lái)學(xué)習(xí);最近我在網(wǎng)上也看了不少文章,今天想跟大家分享一下關(guān)于訪問(wèn)者模式的一些知識(shí),先來(lái)看一個(gè)簡(jiǎn)單的案例吧。
相信大家都去過(guò)醫(yī)院,看完病,醫(yī)生都會(huì)給我們開(kāi)一個(gè)處方單,很多醫(yī)院都存在如下處理流程:劃價(jià)人員拿到處方單之后根據(jù)藥品名稱(chēng)和數(shù)量計(jì)算總價(jià),藥房工作人員根據(jù)藥品名稱(chēng)和數(shù)量準(zhǔn)備藥品。
我們可以將處方單看成一個(gè)藥品信息的集合,里面包含了一種或多種不同類(lèi)型的藥品信息,不同類(lèi)型的工作人員(如劃價(jià)人員和藥房工作人員)在操作同一個(gè)藥品信息集合時(shí)將提供不同的處理方式,而且可能還會(huì)增加新類(lèi)型的工作人員來(lái)操作處方單。
在軟件開(kāi)發(fā)中,有時(shí)候我們也需要處理像處方單這樣的集合對(duì)象結(jié)構(gòu),在該對(duì)象結(jié)構(gòu)中存儲(chǔ)了多個(gè)不同類(lèi)型的對(duì)象信息,而且對(duì)同一對(duì)象結(jié)構(gòu)中的元素的操作方式并不唯一,可能需要提供多種不同的處理方式,還有可能增加新的處理方式。在設(shè)計(jì)模式中,有一種模式可以滿足上述要求,其模式動(dòng)機(jī)就是以不同的方式操作復(fù)雜對(duì)象結(jié)構(gòu),該模式就是我們本章將要介紹的訪問(wèn)者模式。
模式概述
訪問(wèn)者模式(Visitor Pattern):提供一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作表示,它使我們可以在不改變各元素的類(lèi)的前提下定義作用于這些元素的新操作。訪問(wèn)者模式是一種對(duì)象行為型模式。
訪問(wèn)者模式是一種較為復(fù)雜的行為型設(shè)計(jì)模式,它包含訪問(wèn)者和被訪問(wèn)元素兩個(gè)主要組成部分,這些被訪問(wèn)的元素通常具有不同的類(lèi)型,且不同的訪問(wèn)者可以對(duì)它們進(jìn)行不同的訪問(wèn)操作。例如處方單中的各種藥品信息就是被訪問(wèn)的元素,而劃價(jià)人員和藥房工作人員就是訪問(wèn)者。訪問(wèn)者模式使得用戶可以在不修改現(xiàn)有系統(tǒng)的情況下擴(kuò)展系統(tǒng)的功能,為這些不同類(lèi)型的元素增加新的操作。
在使用訪問(wèn)者模式時(shí),被訪問(wèn)元素通常不是單獨(dú)存在的,它們存儲(chǔ)在一個(gè)集合中,這個(gè)集合被稱(chēng)為“對(duì)象結(jié)構(gòu)”,訪問(wèn)者通過(guò)遍歷對(duì)象結(jié)構(gòu)實(shí)現(xiàn)對(duì)其中存儲(chǔ)的元素的逐個(gè)操作。
其結(jié)構(gòu)如下圖所示:

- Vistor(抽象訪問(wèn)者):抽象訪問(wèn)者為對(duì)象結(jié)構(gòu)中每一個(gè)具體元素類(lèi)ConcreteElement聲明一個(gè)訪問(wèn)操作,從這個(gè)操作的名稱(chēng)或參數(shù)類(lèi)型可以清楚知道需要訪問(wèn)的具體元素的類(lèi)型,具體訪問(wèn)者需要實(shí)現(xiàn)這些操作方法,定義對(duì)這些元素的訪問(wèn)操作。
- ConcreteVisitor(具體訪問(wèn)者):具體訪問(wèn)者實(shí)現(xiàn)了每個(gè)由抽象訪問(wèn)者聲明的操作,每一個(gè)操作用于訪問(wèn)對(duì)象結(jié)構(gòu)中一種類(lèi)型的元素。
- Element(抽象元素):抽象元素一般是抽象類(lèi)或者接口,它定義一個(gè)accept()方法,該方法通常以一個(gè)抽象訪問(wèn)者作為參數(shù)。
- ConcreteElement(具體元素):具體元素實(shí)現(xiàn)了accept()方法,在accept()方法中調(diào)用訪問(wèn)者的訪問(wèn)方法以便完成對(duì)一個(gè)元素的操作。
- ObjectStructure(對(duì)象結(jié)構(gòu)):對(duì)象結(jié)構(gòu)是一個(gè)元素的集合,它用于存放元素對(duì)象,并且提供了遍歷其內(nèi)部元素的方法。它可以結(jié)合組合模式來(lái)實(shí)現(xiàn),也可以是一個(gè)簡(jiǎn)單的集合對(duì)象,如一個(gè)List對(duì)象或一個(gè)Set對(duì)象。
適用場(chǎng)景
(1)一個(gè)對(duì)象結(jié)構(gòu)包含多個(gè)類(lèi)型的對(duì)象,希望對(duì)這些對(duì)象實(shí)施一些依賴(lài)其具體類(lèi)型的操作。在訪問(wèn)者中針對(duì)每一種具體的類(lèi)型都提供了一個(gè)訪問(wèn)操作,不同類(lèi)型的對(duì)象可以有不同的訪問(wèn)操作。
(2)需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作,而需要避免讓這些操作“污染”這些對(duì)象的類(lèi),也不希望在增加新操作時(shí)修改這些類(lèi)。訪問(wèn)者模式使得我們可以將相關(guān)的訪問(wèn)操作集中起來(lái)定義在訪問(wèn)者類(lèi)中,對(duì)象結(jié)構(gòu)可以被多個(gè)不同的訪問(wèn)者類(lèi)所使用,將對(duì)象本身與對(duì)象的訪問(wèn)操作分離。
(3)對(duì)象結(jié)構(gòu)中對(duì)象對(duì)應(yīng)的類(lèi)很少改變,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作。
案例場(chǎng)景一
某銀行開(kāi)發(fā)了一套OA系統(tǒng),在該OA系統(tǒng)中包含一個(gè)員工信息管理子系統(tǒng),該銀行員工包括正式員工和臨時(shí)工,每周人力資源部和財(cái)務(wù)部等部門(mén)需要對(duì)員工數(shù)據(jù)進(jìn)行匯總,匯總數(shù)據(jù)包括員工工作時(shí)間、員工工資等。該公司基本制度如下:
(1)正式員工(Full time Employee)每周工作時(shí)間為40小時(shí),不同級(jí)別、不同部門(mén)的員工每周基本工資不同;如果超過(guò)40小時(shí),超出部分按照100元/小時(shí)作為加班費(fèi);如果少于40小時(shí),所缺時(shí)間按照請(qǐng)假處理,請(qǐng)假所扣工資以80元/小時(shí)計(jì)算,直到基本工資扣除到零為止。除了記錄實(shí)際工作時(shí)間外,人力資源部需記錄加班時(shí)長(zhǎng)或請(qǐng)假時(shí)長(zhǎng),作為員工平時(shí)表現(xiàn)的一項(xiàng)依據(jù)。
(2)臨時(shí)工(Part time Employee)每周工作時(shí)間不固定,基本工資按小時(shí)計(jì)算,不同崗位的臨時(shí)工小時(shí)工資不同。人力資源部只需記錄實(shí)際工作時(shí)間。
人力資源部和財(cái)務(wù)部工作人員可以根據(jù)各自的需要對(duì)員工數(shù)據(jù)進(jìn)行匯總處理,人力資源部負(fù)責(zé)匯總每周員工工作時(shí)間,而財(cái)務(wù)部負(fù)責(zé)計(jì)算每周員工工資。
一坨坨代碼實(shí)現(xiàn)
class EmployeeList {
private ArrayList<Employee> list = new ArrayList<Employee>(); //員工集合
//處理員工數(shù)據(jù)
public void handle(String departmentName) {
//財(cái)務(wù)部處理員工數(shù)據(jù)
if (departmentName.equalsIgnoreCase("財(cái)務(wù)部")) {
for (Object obj : list) {
if (obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee")) {
System.out.println("財(cái)務(wù)部處理全職員工數(shù)據(jù)!");
} else {
System.out.println("財(cái)務(wù)部處理兼職員工數(shù)據(jù)!");
}
}
//人力資源部處理員工數(shù)據(jù)
} else if (departmentName.equalsIgnoreCase("人力資源部")) {
for (Object obj : list) {
if (obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee")) {
System.out.println("人力資源部處理全職員工數(shù)據(jù)!");
} else {
System.out.println("人力資源部處理兼職員工數(shù)據(jù)!");
}
}
}
}
}
在handle()方法中,通過(guò)對(duì)部門(mén)名稱(chēng)和員工類(lèi)型進(jìn)行判斷,不同部門(mén)對(duì)不同類(lèi)型的員工進(jìn)行了不同的處理,滿足了員工數(shù)據(jù)匯總的要求。但是該解決方案存在如下幾個(gè)問(wèn)題:
(1)EmployeeList類(lèi)承擔(dān)了過(guò)多的職責(zé),既不方便代碼的復(fù)用,也不利于系統(tǒng)的擴(kuò)展,違背了“單一職責(zé)原則”。
(2)在代碼中包含大量的“if…else…”條件判斷語(yǔ)句,既需要對(duì)不同部門(mén)進(jìn)行判斷,又需要對(duì)不同類(lèi)型的員工進(jìn)行判斷,還將出現(xiàn)嵌套的條件判斷語(yǔ)句,導(dǎo)致測(cè)試和維護(hù)難度增大。
(3)如果要增加一個(gè)新的部門(mén)來(lái)操作員工集合,不得不修改EmployeeList類(lèi)的源代碼,在handle()方法中增加一個(gè)新的條件判斷語(yǔ)句和一些業(yè)務(wù)處理代碼來(lái)實(shí)現(xiàn)新部門(mén)的訪問(wèn)操作。這違背了“開(kāi)閉原則”,系統(tǒng)的靈活性和可擴(kuò)展性有待提高。
(4)如果要增加一種新類(lèi)型的員工,同樣需要修改EmployeeList類(lèi)的源代碼,在不同部門(mén)的處理代碼中增加對(duì)新類(lèi)型員工的處理邏輯,這也違背了“開(kāi)閉原則”。
重構(gòu)代碼
定義抽象元素
//員工類(lèi):抽象元素類(lèi)
public interface Employee {
void accept(Department handler); //接受一個(gè)抽象訪問(wèn)者訪問(wèn)
}
具體元素類(lèi)
//全職員工類(lèi):具體元素類(lèi)
public class FullTimeEmployee implements Employee {
private String name;
private double weeklyWage;
private int workTime;
public FullTimeEmployee(String name, double weeklyWage, int workTime) {
this.name = name;
this.weeklyWage = weeklyWage;
this.workTime = workTime;
}
public void setName(String name) {
this.name = name;
}
public void setWeeklyWage(double weeklyWage) {
this.weeklyWage = weeklyWage;
}
public void setWorkTime(int workTime) {
this.workTime = workTime;
}
public String getName() {
return (this.name);
}
public double getWeeklyWage() {
return (this.weeklyWage);
}
public int getWorkTime() {
return (this.workTime);
}
public void accept(Department handler) {
handler.visit(this); //調(diào)用訪問(wèn)者的訪問(wèn)方法
}
}
//兼職員工類(lèi):具體元素類(lèi)
public class PartTimeEmployee implements Employee {
private String name;
private double hourWage;
private int workTime;
public PartTimeEmployee(String name, double hourWage, int workTime) {
this.name = name;
this.hourWage = hourWage;
this.workTime = workTime;
}
public void setName(String name) {
this.name = name;
}
public void setHourWage(double hourWage) {
this.hourWage = hourWage;
}
public void setWorkTime(int workTime) {
this.workTime = workTime;
}
public String getName() {
return (this.name);
}
public double getHourWage() {
return (this.hourWage);
}
public int getWorkTime() {
return (this.workTime);
}
public void accept(Department handler) {
handler.visit(this); //調(diào)用訪問(wèn)者的訪問(wèn)方法
}
}
定義抽象訪問(wèn)者類(lèi)
//部門(mén)類(lèi):抽象訪問(wèn)者類(lèi)
public abstract class Department {
//聲明一組重載的訪問(wèn)方法,用于訪問(wèn)不同類(lèi)型的具體元素
public abstract void visit(FullTimeEmployee employee);
public abstract void visit(PartTimeEmployee employee);
}
具體訪問(wèn)者類(lèi)
//財(cái)務(wù)部類(lèi):具體訪問(wèn)者類(lèi)
public class FADepartment extends Department {
//實(shí)現(xiàn)財(cái)務(wù)部對(duì)全職員工的訪問(wèn)
public void visit(FullTimeEmployee employee) {
int workTime = employee.getWorkTime();
double weekWage = employee.getWeeklyWage();
if (workTime > 40) {
weekWage = weekWage + (workTime - 40) * 100;
} else if (workTime < 40) {
weekWage = weekWage - (40 - workTime) * 80;
if (weekWage < 0) {
weekWage = 0;
}
}
System.out.println("正式員工" + employee.getName() + "實(shí)際工資為:" + weekWage + "元。");
}
//實(shí)現(xiàn)財(cái)務(wù)部對(duì)兼職員工的訪問(wèn)
public void visit(PartTimeEmployee employee) {
int workTime = employee.getWorkTime();
double hourWage = employee.getHourWage();
System.out.println("臨時(shí)工" + employee.getName() + "實(shí)際工資為:" + workTime * hourWage + "元。");
}
}
//人力資源部類(lèi):具體訪問(wèn)者類(lèi)
public class HRDepartment extends Department {
//實(shí)現(xiàn)人力資源部對(duì)全職員工的訪問(wèn)
public void visit(FullTimeEmployee employee) {
int workTime = employee.getWorkTime();
System.out.println("正式員工" + employee.getName() + "實(shí)際工作時(shí)間為:" + workTime + "小時(shí)。");
if (workTime > 40) {
System.out.println("正式員工" + employee.getName() + "加班時(shí)間為:" + (workTime - 40) + "小時(shí)。");
} else if (workTime < 40) {
System.out.println("正式員工" + employee.getName() + "請(qǐng)假時(shí)間為:" + (40 - workTime) + "小時(shí)。");
}
}
//實(shí)現(xiàn)人力資源部對(duì)兼職員工的訪問(wèn)
public void visit(PartTimeEmployee employee) {
int workTime = employee.getWorkTime();
System.out.println("臨時(shí)工" + employee.getName() + "實(shí)際工作時(shí)間為:" + workTime + "小時(shí)。");
}
}
定義數(shù)據(jù)結(jié)構(gòu)以及測(cè)試
//員工列表類(lèi):對(duì)象結(jié)構(gòu)
class EmployeeList {
//定義一個(gè)集合用于存儲(chǔ)員工對(duì)象
private ArrayList<Employee> list = new ArrayList<>();
public EmployeeList(){
list.add(new FullTimeEmployee("張三",3200.00,45));
list.add(new FullTimeEmployee("李四",2500.00,40));
list.add(new PartTimeEmployee("王二",80.00,20));
list.add(new PartTimeEmployee("李強(qiáng)",100.00,30));
}
//遍歷訪問(wèn)員工集合中的每一個(gè)員工對(duì)象
public void accept(Department handler) {
for (Object obj : list) {
((Employee) obj).accept(handler);
}
}
public static void main(String[] args) {
EmployeeList employeeList = new EmployeeList();
System.out.println("\r\n財(cái)務(wù)部:");
employeeList.accept(new FADepartment());
System.out.println("\r\n人力資源部:");
employeeList.accept(new HRDepartment());
}
}
測(cè)試結(jié)果如下:
財(cái)務(wù)部:
正式員工張三實(shí)際工資為:3700.0元。
正式員工李四實(shí)際工資為:2500.0元。
臨時(shí)工王二實(shí)際工資為:1600.0元。
臨時(shí)工李強(qiáng)實(shí)際工資為:3000.0元。
人力資源部:
正式員工張三實(shí)際工作時(shí)間為:45小時(shí)。
正式員工張三加班時(shí)間為:5小時(shí)。
正式員工李四實(shí)際工作時(shí)間為:40小時(shí)。
臨時(shí)工王二實(shí)際工作時(shí)間為:20小時(shí)。
臨時(shí)工李強(qiáng)實(shí)際工作時(shí)間為:30小時(shí)。
從以上的業(yè)務(wù)場(chǎng)景中可以看到,在嵌?訪問(wèn)者模式后,可以讓整個(gè)?程結(jié)構(gòu)變得容易添加和修改。
如果要在系統(tǒng)中增加一種新的訪問(wèn)者,無(wú)須修改源代碼,只要增加一個(gè)新的具體訪問(wèn)者類(lèi)即可,在該具體訪問(wèn)者中封裝了新的操作元素對(duì)象的方法。從增加新的訪問(wèn)者的角度來(lái)看,訪問(wèn)者模式符合“開(kāi)閉原則”。
如果要在系統(tǒng)中增加一種新的具體元素,例如增加一種新的員工類(lèi)型為“退休人員”,由于原有系統(tǒng)并未提供相應(yīng)的訪問(wèn)接口(在抽象訪問(wèn)者中沒(méi)有聲明任何訪問(wèn)“退休人員”的方法),因此必須對(duì)原有系統(tǒng)進(jìn)行修改,在原有的抽象訪問(wèn)者類(lèi)和具體訪問(wèn)者類(lèi)中增加相應(yīng)的訪問(wèn)方法。從增加新的元素的角度來(lái)看,訪問(wèn)者模式違背了“開(kāi)閉原則”。
綜上所述,訪問(wèn)者模式與抽象工廠模式類(lèi)似,對(duì)“開(kāi)閉原則”的支持具有傾斜性,可以很方便地添加新的訪問(wèn)者,但是添加新的元素較為麻煩。
案例場(chǎng)景二
校園中有學(xué)生和老師兩種身份的用戶,那么對(duì)于家長(zhǎng)和校長(zhǎng)關(guān)心的角度來(lái)看,他們的視角是不同的。家長(zhǎng)更關(guān)心孩子的成績(jī)和老師的能力,校長(zhǎng)更關(guān)心老師所在班級(jí)學(xué)生的人數(shù)和升學(xué)率。
那么這樣學(xué)生和老師就是一個(gè)固定信息的內(nèi)容,而想讓不同視角的用戶獲取關(guān)心的信息,就比較適合使用訪問(wèn)者模式來(lái)實(shí)現(xiàn),從而讓實(shí)體與業(yè)務(wù)解耦,增強(qiáng)擴(kuò)展性。
代碼實(shí)現(xiàn)
定義用戶抽象類(lèi)
// 基礎(chǔ)用戶信息
public abstract class User {
public String name; // 姓名
public String identity; // 身份;重點(diǎn)班、普通班 | 特級(jí)教師、普通教師、實(shí)習(xí)教師
public String clazz; // 班級(jí)
public User(String name, String identity, String clazz) {
this.name = name;
this.identity = identity;
this.clazz = clazz;
}
// 核心訪問(wèn)方法
public abstract void accept(Visitor visitor);
}
實(shí)現(xiàn)用戶信息(老師和學(xué)生)
老師類(lèi)
public class Teacher extends User {
public Teacher(String name, String identity, String clazz) {
super(name, identity, clazz);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
// 升本率
public double entranceRatio() {
return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
學(xué)生類(lèi)
public class Student extends User {
public Student(String name, String identity, String clazz) {
super(name, identity, clazz);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
//排名
public int ranking() {
return (int) (Math.random() * 100);
}
}
定義訪問(wèn)數(shù)據(jù)接口
public interface Visitor {
// 訪問(wèn)學(xué)生信息
void visit(Student student);
// 訪問(wèn)老師信息
void visit(Teacher teacher);
}
實(shí)現(xiàn)訪問(wèn)類(lèi)型(校長(zhǎng)和家長(zhǎng))
訪問(wèn)者:校長(zhǎng)
public class Principal implements Visitor {
private Logger logger = LoggerFactory.getLogger(Principal.class);
public void visit(Student student) {
logger.info("學(xué)生信息 姓名:{} 班級(jí):{}", student.name, student.clazz);
}
public void visit(Teacher teacher) {
logger.info("學(xué)生信息 姓名:{} 班級(jí):{} 升學(xué)率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());
}
}
訪問(wèn)者:家長(zhǎng)
public class Parent implements Visitor {
private Logger logger = LoggerFactory.getLogger(Parent.class);
public void visit(Student student) {
logger.info("學(xué)生信息 姓名:{} 班級(jí):{} 排名:{}", student.name, student.clazz, student.ranking());
}
public void visit(Teacher teacher) {
logger.info("老師信息 姓名:{} 班級(jí):{} 級(jí)別:{}", teacher.name, teacher.clazz, teacher.identity);
}
}
數(shù)據(jù)結(jié)構(gòu)
public class DataView {
List<User> userList = new ArrayList<User>();
public DataView() {
userList.add(new Student("小馬", "重點(diǎn)班", "一年一班"));
userList.add(new Student("小張", "重點(diǎn)班", "一年一班"));
userList.add(new Student("小劉", "普通班", "二年三班"));
userList.add(new Student("小董", "普通班", "三年四班"));
userList.add(new Teacher("Tom", "特級(jí)教師", "一年一班"));
userList.add(new Teacher("Jack", "特級(jí)教師", "一年一班"));
userList.add(new Teacher("Rose", "普通教師", "二年三班"));
userList.add(new Teacher("Bob", "實(shí)習(xí)教師", "三年四班"));
}
// 展示
public void show(Visitor visitor) {
for (User user : userList) {
user.accept(visitor);
}
}
}
編寫(xiě)測(cè)試類(lèi)
@Test
public void test(){
DataView dataView = new DataView();
logger.info("\r\n家長(zhǎng)視角訪問(wèn):");
dataView.show(new Parent()); // 家長(zhǎng)
logger.info("\r\n校長(zhǎng)視角訪問(wèn):");
dataView.show(new Principal()); // 校長(zhǎng)
}
測(cè)試結(jié)果
家長(zhǎng)視角訪問(wèn):
12:33:21.336 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:小馬 班級(jí):一年一班 排名:58
12:33:21.336 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:小張 班級(jí):一年一班 排名:17
12:33:21.336 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:小劉 班級(jí):二年三班 排名:29
12:33:21.336 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:小董 班級(jí):三年四班 排名:67
12:33:21.336 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:Tom 班級(jí):一年一班 級(jí)別:特級(jí)教師
12:33:21.336 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:Jack 班級(jí):一年一班 級(jí)別:特級(jí)教師
12:33:21.337 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:Rose 班級(jí):二年三班 級(jí)別:普通教師
12:33:21.337 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:Bob 班級(jí):三年四班 級(jí)別:實(shí)習(xí)教師
校長(zhǎng)視角訪問(wèn):
12:33:21.338 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:小馬 班級(jí):一年一班
12:33:21.339 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:小張 班級(jí):一年一班
12:33:21.339 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:小劉 班級(jí):二年三班
12:33:21.339 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:小董 班級(jí):三年四班
12:33:21.342 [main] INFO o.i.d.design.visitor.impl.Principal - 老師信息 姓名:Tom 班級(jí):一年一班 升學(xué)率:61.33
12:33:21.343 [main] INFO o.i.d.design.visitor.impl.Principal - 老師信息 姓名:Jack 班級(jí):一年一班 升學(xué)率:83.64
12:33:21.343 [main] INFO o.i.d.design.visitor.impl.Principal - 老師信息 姓名:Rose 班級(jí):二年三班 升學(xué)率:87.57
12:33:21.343 [main] INFO o.i.d.design.visitor.impl.Principal - 老師信息 姓名:Bob 班級(jí):三年四班 升學(xué)率:30.34
- 通過(guò)測(cè)試結(jié)果可以看到,家長(zhǎng)和校長(zhǎng)的訪問(wèn)視角同步,數(shù)據(jù)也是差異化的。
- 通過(guò)這樣的測(cè)試結(jié)果,可以看到訪問(wèn)者模式的初心和結(jié)果,在適合的場(chǎng)景運(yùn)用合適的模式,非常有利于程序開(kāi)發(fā)。
總結(jié)
由于訪問(wèn)者模式的使用條件較為苛刻,本身結(jié)構(gòu)也較為復(fù)雜,因此在實(shí)際應(yīng)用中使用頻率不是特別高。當(dāng)系統(tǒng)中存在一個(gè)較為復(fù)雜的對(duì)象結(jié)構(gòu),且不同訪問(wèn)者對(duì)其所采取的操作也不相同時(shí),可以考慮使用訪問(wèn)者模式進(jìn)行設(shè)計(jì)。在XML文檔解析、編譯器的設(shè)計(jì)、復(fù)雜集合對(duì)象的處理等領(lǐng)域訪問(wèn)者模式得到了一定的應(yīng)用。
好的學(xué)習(xí)?式才好更容易接受知識(shí),學(xué)習(xí)編程的更需要的不單單是看,?是操作。??多種設(shè)計(jì)模式每?種都有??的設(shè)計(jì)技巧,也可以說(shuō)是巧妙之處,這些巧妙的地?往往是解決復(fù)雜難題的最佳視?。親力親為,才能為所欲為,為了??的欲望而努?!