訪問者模式包含以下幾個角色:java
●Vistor(抽象訪問者):抽象訪問者爲對象結構中每個具體元素類ConcreteElement聲明一個訪問操做,從這個操做的名稱或參數類型能夠清楚知道須要訪問的具體元素的類型,具體訪問者須要實現這些操做方法,定義對這些元素的訪問操做。 ●ConcreteVisitor(具體訪問者):具體訪問者實現了每一個由抽象訪問者聲明的操做,每個操做用於訪問對象結構中一種類型的元素。 ●Element(抽象元素):抽象元素通常是抽象類或者接口,它定義一個accept()方法,該方法一般以一個抽象訪問者做爲參數。 ●ConcreteElement(具體元素):具體元素實現了accept()方法,在accept()方法中調用訪問者的訪問方法以便完成對一個元素的操做。 ●ObjectStructure(對象結構):對象結構是一個元素的集合,它用於存放元素對象,而且提供了遍歷其內部元素的方法。它能夠結合組合模式來實現,也能夠是一個簡單的集合對象,如一個List對象或一個Set對象
一、主要優勢測試
(1) 增長新的訪問操做很方便。使用訪問者模式,增長新的訪問操做就意味着增長一個新的具體訪問者類,實現簡單,無須修改源代碼,符合「開閉原則」。 (2) 將有關元素對象的訪問行爲集中到一個訪問者對象中,而不是分散在一個個的元素類中。類的職責更加清晰,有利於對象結構中元素對象的複用,相同的對象結構能夠供多個不一樣的訪問者訪問。 (3) 讓用戶可以在不修改現有元素類層次結構的狀況下,定義做用於該層次結構的操做
二、主要缺點this
(1) 增長新的元素類很困難。在訪問者模式中,每增長一個新的元素類都意味着要在抽象訪問者角色中增長一個新的抽象操做,並在每個具體訪問者類中增長相應的具體操做,這違背了「開閉原則」的要求。 (2) 破壞封裝。訪問者模式要求訪問者對象訪問並調用每個元素對象的操做,這意味着元素對象有時候必須暴露一些本身的內部操做和內部狀態,不然沒法供訪問者訪問
三、適用場景code
(1) 一個對象結構包含多個類型的對象,但願對這些對象實施一些依賴其具體類型的操做。在訪問者中針對每一種具體的類型都提供了一個訪問操做,不一樣類型的對象能夠有不一樣的訪問操做。 (2) 須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而須要避免讓這些操做「污染」這些對象的類,也不但願在增長新操做時修改這些類。訪問者模式使得咱們能夠將相關的訪問操做集中起來定義在訪問者類中,對象結構能夠被多個不一樣的訪問者類所使用,將對象自己與對象的訪問操做分離。 (3) 對象結構中對象對應的類不多改變,但常常須要在此對象結構上定義新的操做
FinanceDepartment表示財務部,HRDepartment表示人力資源部,它們充當具體訪問者的角色,其抽象父類Department充當抽象訪問者角色;EmployeeList充當對象結構,用於存儲員工列表;FullTimeEmployee表示全職員工,PartTimeEmployee表示兼職員工,它們充當具體元素角色,而其父類IEmployee(這裏實現形式是interface)充當抽象元素角色。對象
/** * 2018/12/6 * 抽象元素類:Employee * * @author machuanpeng */ public interface IEmployee { void accept(Department handler); }
/** * 2018/12/6 * FullTimeEmployee * * @author machuanpeng */ public class FullTimeEmployee implements IEmployee { public string name; public double weeklyWage; public int workTime; public FullTimeEmployee(string name, double weeklyWage, int workTime){ this.name = name; this.weeklyWage = weeklyWage; this.workTime = workTime; } public void accept(Department handler){ handler.visit(this); } }
/** * 2018/12/6 * PartTimeEmployee * * @author machuanpeng */ public class PartTimeEmployee implements IEmployee { public string name; public double hourWage; public int workTime; public PartTimeEmployee(string name, double hourWage, int workTime){ this.name = name; this.hourWage = hourWage; this.workTime = workTime; } public void accept(Department handler){ handler.visit(this); } }
/** * 2018/12/6 * 對象結構類 * * @author machuanpeng */ public class EmployeeList { private List<IEmployee> empList = new ArrayList<IEmployee>(); public void addEmployee(IEmployee emp){ this.empList.add(emp); } public void accept(Department handler){ for(IEmployee emp : empList) { emp.accept(handler); } } }
/** * 2018/12/6 * 抽象訪問者類 * * @author machuanpeng */ public abstract class Department { // 聲明一組重載的訪問方法,用於訪問不一樣類型的具體元素 public abstract void visit(FullTimeEmployee employee); public abstract void visit(PartTimeEmployee employee); }
/** * 2018/12/6 * FinanceDepartment * * @author machuanpeng */ public class FinanceDepartment extends Department { // 實現財務部對兼職員工數據的訪問 public void visit(PartTimeEmployee employee){ String name = employee.name; int workTime = employee.workTime; double hourWage = employee.hourWage; System.out.println("臨時工" + name+"實際工資爲:"+workTime * hourWage+"元"); } // 實現財務部對全職員工數據的訪問 public void visit(FullTimeEmployee employee) { String name = employee.name; int workTime = employee.workTime; double weekWage = employee.weeklyWage; if (workTime > 40) { weekWage = weekWage + (workTime - 40) * 50; } else if (workTime < 40) { weekWage = weekWage - (40 - workTime) * 80; if (weekWage < 0) { weekWage = 0; } } System.out.println("正式工" + name+"實際工資爲:"+weekWage+"元"); } }
/** * 2018/12/6 * HRDepartment * * @author machuanpeng */ public class HRDepartment extends Department { // 實現人力資源部對兼職員工數據的訪問 public void visit(PartTimeEmployee employee){ String name = employee.name; int workTime = employee.workTime; System.out.println("臨時工" + name+"實際工做時間爲:"+workTime+"小時"); } // 實現人力資源部對全職員工數據的訪問 public void visit(FullTimeEmployee employee) { String name = employee.name; int workTime = employee.workTime; System.out.println("正式工" + name+"實際工做時間爲:"+workTime+"小時"); if (workTime > 40) { int addTime = workTime-40; System.out.println("正式工" + name+"加班時間爲:"+addTime+"小時"); } else if (workTime < 40) { int delTime = 40-workTime; System.out.println("正式工" + name+"請假時間爲:"+delTime+"小時"); } } }
測試:blog
public class Client { public static void main(String args[]) { EmployeeList empList = new EmployeeList(); IEmployee fteA = new FullTimeEmployee("趙", 3200.00, 45); IEmployee fteB = new FullTimeEmployee("錢", 2000, 40); IEmployee fteC = new FullTimeEmployee("孫", 2400, 38); IEmployee fteD = new PartTimeEmployee("李", 80, 20); IEmployee fteE = new PartTimeEmployee("周", 60, 18); empList.addEmployee(fteA); empList.addEmployee(fteB); empList.addEmployee(fteC); empList.addEmployee(fteD); empList.addEmployee(fteE); Department dept = new FinanceDepartment(); empList.accept(dept); dept = new HRDepartment(); empList.accept(dept); } }