一、概述
訪問者模式是一種較爲複雜的行爲型設計模式,它包含訪問者和被訪問元素兩個主要組成部分,這些被訪問的元素通常具有不同的類型,且不同的訪問者可以對它們進行不同的訪問操作。在使用訪問者模式時,被訪問元素通常不是單獨存在的,它們存儲在一個集合中,這個集合被稱爲“對象結構”,訪問者通過遍歷對象結構實現對其中存儲的元素的逐個操作。訪問者模式是一種對象行爲型模式。
二、適用場景
當有多種類型的訪問者(或是操作者) 對一組被訪問者對象集合(或是對象結構)進行操作(其中對象集合也包含多種類型對象),不同的訪問者類型對每一種具體的被訪問者對象提供不同的訪問操作,每種訪問者類型對象對不同的被訪問者也有不同的訪問操作,那麼這種場景就非常適用訪問者模式。
如果前面這幾句比較繞的文字說明沒看明白,那麼小呂就舉例一個生活中的業務場景:
你所在的公司每個月人力資源部要對所有員工進行上班時長、加班時長統計,而財務部要對所有員工進行工資覈算,不同職位的員工薪資覈算標準肯定不一樣啊,這個大家都明白。在這個案例中人力資源部和財務部是兩個不同類型的部門(訪問者),所有員工(被訪問者)是一個對象集合,而員工又劃分爲管理者和技術者兩種類型(備註:這裏小呂只是簡單劃分爲兩類),在每月的統計中,人力資源部需要分別對員工進行上班時長和加班時長進行統計,而財務部需要對不同職位的員工進行薪資覈算,可見不同部門職責不同,及對員工的訪問操作不同、每個部門對不同類型的員工的訪問操作也不同。那麼針對這種場景 我們有必要了解一下訪問者模式。
三、UML類圖
四、參與者
1>、Visitor(抽象訪問者):爲每種具體的被訪問者(ConcreteElement)聲明一個訪問操作;
2>、ConcreteVisitor(具體訪問者):實現對被訪問者(ConcreteElement)的具體訪問操作;
3>、Element(抽象被訪問者):通常有一個Accept方法,用來接收/引用一個抽象訪問者對象;
4>、ConcreteElement(具體被訪問者對象):實現Accept抽象方法,通過傳入的具體訪問者參數、調用具體訪問者對該對象的訪問操作方法實現訪問邏輯;
5>、Clent、ObjectStructure(客戶端訪問過程測試環境):該過程中,被訪問者通常爲一個集合對象,通過對集合的遍歷完成訪問者對每一個被訪問元素的訪問操作;
五、用例學習
1.抽象被訪問者:公司員工抽象類 Employee.java
/**
* 公司員工(被訪問者)抽象類
* @author [email protected]
*
*/
public abstract class Employee {
/**
* 接收/引用一個抽象訪問者對象
* @param department 抽象訪問者 這裏指的是公司部門如 人力資源部、財務部
*/
public abstract void accept(Department department);
}
2.具體被訪問者:公司管理崗位員工類 ManagerEmployee.java
/**
* 公司員工:管理者(具體的被訪問者對象)
* @author [email protected]
*
*/
public class ManagerEmployee extends Employee {
// 員工姓名
private String name;
// 每天上班時長
private int timeSheet;
// 每月工資
private double wage;
// 請假/遲到 懲罰時長
private int punishmentTime;
public ManagerEmployee(String name, int timeSheet, double wage, int punishmentTime) {
this.name = name;
this.timeSheet = timeSheet;
this.wage = wage;
this.punishmentTime = punishmentTime;
}
@Override
public void accept(Department department) {
department.visit(this);
}
/**
* 獲取每月的上班實際時長 = 每天上班時長 * 每月上班天數 - 懲罰時長
* @return
*/
public int getTotalTimeSheet(){
return timeSheet * 22 - punishmentTime;
}
/**
* 獲取每月實際應發工資 = 每月固定工資 - 懲罰時長 * 5<br/>
* <作爲公司管理者 每遲到1小時 扣5塊錢>
* @return
*/
public double getTotalWage(){
return wage - punishmentTime * 5;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWage() {
return wage;
}
public void setWage(double wage) {
this.wage = wage;
}
public int getPunishmentTime() {
return punishmentTime;
}
public void setPunishmentTime(int punishmentTime) {
this.punishmentTime = punishmentTime;
}
}
3.具體被訪問者:公司普通崗位員工類 GeneralEmployee.java
/**
* 公司普通員工(具體的被訪問者對象)
* @author [email protected]
*
*/
public class GeneralEmployee extends Employee {
// 員工姓名
private String name;
// 每天上班時長
private int timeSheet;
// 每月工資
private double wage;
// 請假/遲到 懲罰時長
private int punishmentTime;
public GeneralEmployee(String name, int timeSheet, double wage, int punishmentTime) {
this.name = name;
this.timeSheet = timeSheet;
this.wage = wage;
this.punishmentTime = punishmentTime;
}
@Override
public void accept(Department department) {
department.visit(this);
}
/**
* 獲取每月的上班實際時長 = 每天上班時長 * 每月上班天數 - 懲罰時長
* @return
*/
public int getTotalTimeSheet() {
return timeSheet * 22 - punishmentTime;
}
/**
* 獲取每月實際應發工資 = 每月固定工資 - 懲罰時長 * 10<br/>
* <作爲公司普通員工 每遲到1小時 扣10塊錢 坑吧? 哈哈>
*
* @return
*/
public double getTotalWage() {
return wage - punishmentTime * 10;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWage() {
return wage;
}
public void setWage(double wage) {
this.wage = wage;
}
public int getPunishmentTime() {
return punishmentTime;
}
public void setPunishmentTime(int punishmentTime) {
this.punishmentTime = punishmentTime;
}
}
4.抽象訪問者:公司部門抽象類 Department.java
/**
* 公司部門(訪問者)抽象類
* @author [email protected]
*
*/
public abstract class Department {
// 聲明一組重載的訪問方法,用於訪問不同類型的具體元素(這裏指的是不同的員工)
/**
* 抽象方法 訪問公司管理者對象<br/>
* 具體訪問對象的什麼 就由具體的訪問者子類(這裏指的是不同的具體部門)去實現
* @param me
*/
public abstract void visit(ManagerEmployee me);
/**
* 抽象方法 訪問公司普通員工對象<br/>
* 具體訪問對象的什麼 就由具體的訪問者子類(這裏指的是不同的具體部門)去實現
* @param ge
*/
public abstract void visit(GeneralEmployee ge);
}
5.具體訪問者:公司財務部類 FADepartment.java
/**
* 具體訪問者對象:公司財務部<br/>
* 財務部的職責就是負責統計覈算員工的工資
* @author [email protected]
*
*/
public class FADepartment extends Department {
/**
* 訪問公司管理者對象的每月工資
*/
@Override
public void visit(ManagerEmployee me) {
double totalWage = me.getTotalWage();
System.out.println("管理者: " + me.getName() +
" 固定工資 =" + me.getWage() +
", 遲到時長 " + me.getPunishmentTime() + "小時"+
", 實發工資="+totalWage);
}
/**
* 訪問公司普通員工對象的每月工資
*/
@Override
public void visit(GeneralEmployee ge) {
double totalWage = ge.getTotalWage();
System.out.println("普通員工: " + ge.getName() +
" 固定工資 =" + ge.getWage() +
", 遲到時長 " + ge.getPunishmentTime() + "小時"+
", 實發工資="+totalWage);
}
}
6.具體訪問者:公司人力資源部類 HRDepartment.java
/**
* 具體訪問者對象:公司人力資源部<br/>
* 人力資源部的職責就是負責統計覈算員工的每月上班時長
* @author [email protected]
*
*/
public class HRDepartment extends Department {
/**
* 訪問公司管理者對象的每月實際上班時長統計
*/
@Override
public void visit(ManagerEmployee me) {
me.getTotalTimeSheet();
}
/**
* 訪問公司普通員工對象的每月實際上班時長統計
*/
@Override
public void visit(GeneralEmployee ge) {
ge.getTotalTimeSheet();
}
}
7.客戶端測試類:模擬財務部對公司員工的工資覈算和訪問 Client.java
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
List<Employee> employeeList = new ArrayList<Employee>();
Employee mep1,mep2,gep1,gep2,gep3;
// 管理者1
mep1 = new ManagerEmployee("王總", 8, 20000, 10);
// 管理者2
mep2 = new ManagerEmployee("謝經理", 8, 15000, 15);
// 普通員工1
gep1 = new GeneralEmployee("小杰", 8, 8000, 8);
// 普通員工2
gep2 = new GeneralEmployee("小曉", 8, 8500, 12);
// 普通員工3
gep3 = new GeneralEmployee("小虎", 8, 7500, 0);
employeeList.add(mep1);
employeeList.add(mep2);
employeeList.add(gep1);
employeeList.add(gep2);
employeeList.add(gep3);
// 財務部 對公司員工的工資覈算/訪問
FADepartment department = new FADepartment();
for(Employee employee : employeeList){
employee.accept(department);
}
}
}
如果要更改爲人力資源部對員工的一個月的上班時長統計 則只要將上述代碼中的
FADepartment department = new FADepartment();
修改爲如下即可HRDepartment department = new HRDepartment();
8.程序運行結果:
管理者: 王總 固定工資 =20000.0, 遲到時長 10小時, 實發工資=19950.0
管理者: 謝經理 固定工資 =15000.0, 遲到時長 15小時, 實發工資=14925.0
普通員工: 小杰 固定工資 =8000.0, 遲到時長 8小時, 實發工資=7920.0
普通員工: 小曉 固定工資 =8500.0, 遲到時長 12小時, 實發工資=8380.0
普通員工: 小虎 固定工資 =7500.0, 遲到時長 0小時, 實發工資=7500.0
六、其他