# 操作復雜對象結構——訪問者模式(一)
想必大家都去過醫院,雖然沒有人喜歡去醫院(愛崗敬業的醫務工作人員除外,微笑)。在醫生開具處方單(藥單)后,很多醫院都存在如下處理流程:劃價人員拿到處方單之后根據藥品名稱和數量計算總價,藥房工作人員根據藥品名稱和數量準備藥品,如圖26-1所示:

在圖26-1中,我們可以將處方單看成一個藥品信息的集合,里面包含了一種或多種不同類型的藥品信息,不同類型的工作人員(如劃價人員和藥房工作人員)在操作同一個藥品信息集合時將提供不同的處理方式,而且可能還會增加新類型的工作人員來操作處方單。
在軟件開發中,有時候我們也需要處理像處方單這樣的集合對象結構,在該對象結構中存儲了多個不同類型的對象信息,而且對同一對象結構中的元素的操作方式并不唯一,可能需要提供多種不同的處理方式,還有可能增加新的處理方式。在設計模式中,有一種模式可以滿足上述要求,其模式動機就是以不同的方式操作復雜對象結構,該模式就是我們本章將要介紹的訪問者模式。
26.1 OA系統中員工數據匯總
Sunny軟件公司欲為某銀行開發一套OA系統,在該OA系統中包含一個員工信息管理子系統,該銀行員工包括正式員工和臨時工,每周人力資源部和財務部等部門需要對員工數據進行匯總,匯總數據包括員工工作時間、員工工資等。該公司基本制度如下:
(1) 正式員工(Full time Employee)每周工作時間為40小時,不同級別、不同部門的員工每周基本工資不同;如果超過40小時,超出部分按照100元/小時作為加班費;如果少于40小時,所缺時間按照請假處理,請假所扣工資以80元/小時計算,直到基本工資扣除到零為止。除了記錄實際工作時間外,人力資源部需記錄加班時長或請假時長,作為員工平時表現的一項依據。
(2) 臨時工(Part time Employee)每周工作時間不固定,基本工資按小時計算,不同崗位的臨時工小時工資不同。人力資源部只需記錄實際工作時間。
人力資源部和財務部工作人員可以根據各自的需要對員工數據進行匯總處理,人力資源部負責匯總每周員工工作時間,而財務部負責計算每周員工工資。
Sunny軟件公司開發人員針對上述需求,提出了一個初始解決方案,其核心代碼如下所示:
```
import java.util.*;
class EmployeeList
{
private ArrayList<Employee> list = new ArrayList<Employee>(); //員工集合
//增加員工
public void addEmployee(Employee employee)
{
list.add(employee);
}
//處理員工數據
public void handle(String departmentName)
{
if(departmentName.equalsIgnoreCase("財務部")) //財務部處理員工數據
{
for(Object obj : list)
{
if(obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee"))
{
System.out.println("財務部處理全職員工數據!");
}
else
{
System.out.println("財務部處理兼職員工數據!");
}
}
}
else if(departmentName.equalsIgnoreCase("人力資源部")) //人力資源部處理員工數據
{
for(Object obj : list)
{
if(obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee"))
{
System.out.println("人力資源部處理全職員工數據!");
}
else
{
System.out.println("人力資源部處理兼職員工數據!");
}
}
}
}
}
```
在EmployeeList類的handle()方法中,通過對部門名稱和員工類型進行判斷,不同部門對不同類型的員工進行了不同的處理,滿足了員工數據匯總的要求。但是該解決方案存在如下幾個問題:
(1) EmployeeList類非常龐大,它將各個部門處理各類員工數據的代碼集中在一個類中,在具體實現時,代碼將相當冗長,EmployeeList類承擔了過多的職責,既不方便代碼的復用,也不利于系統的擴展,違背了“單一職責原則”。
(2)在代碼中包含大量的“if…else…”條件判斷語句,既需要對不同部門進行判斷,又需要對不同類型的員工進行判斷,還將出現嵌套的條件判斷語句,導致測試和維護難度增大。
(3)如果要增加一個新的部門來操作員工集合,不得不修改EmployeeList類的源代碼,在handle()方法中增加一個新的條件判斷語句和一些業務處理代碼來實現新部門的訪問操作。這違背了“開閉原則”,系統的靈活性和可擴展性有待提高。
(4)如果要增加一種新類型的員工,同樣需要修改EmployeeList類的源代碼,在不同部門的處理代碼中增加對新類型員工的處理邏輯,這也違背了“開閉原則”。
如何解決上述問題?如何為同一集合對象中的元素提供多種不同的操作方式?訪問者模式就是一個值得考慮的解決方案,它可以在一定程度上解決上述問題(解決大部分問題)。訪問者模式可以為為不同類型的元素提供多種訪問操作方式,而且可以在不修改原有系統的情況下增加新的操作方式。
- Introduction
- 基礎知識
- 設計模式概述
- 從招式與內功談起——設計模式概述(一)
- 從招式與內功談起——設計模式概述(二)
- 從招式與內功談起——設計模式概述(三)
- 面向對象設計原則
- 面向對象設計原則之單一職責原則
- 面向對象設計原則之開閉原則
- 面向對象設計原則之里氏代換原則
- 面向對象設計原則之依賴倒轉原則
- 面向對象設計原則之接口隔離原則
- 面向對象設計原則之合成復用原則
- 面向對象設計原則之迪米特法則
- 六個創建型模式
- 簡單工廠模式-Simple Factory Pattern
- 工廠三兄弟之簡單工廠模式(一)
- 工廠三兄弟之簡單工廠模式(二)
- 工廠三兄弟之簡單工廠模式(三)
- 工廠三兄弟之簡單工廠模式(四)
- 工廠方法模式-Factory Method Pattern
- 工廠三兄弟之工廠方法模式(一)
- 工廠三兄弟之工廠方法模式(二)
- 工廠三兄弟之工廠方法模式(三)
- 工廠三兄弟之工廠方法模式(四)
- 抽象工廠模式-Abstract Factory Pattern
- 工廠三兄弟之抽象工廠模式(一)
- 工廠三兄弟之抽象工廠模式(二)
- 工廠三兄弟之抽象工廠模式(三)
- 工廠三兄弟之抽象工廠模式(四)
- 工廠三兄弟之抽象工廠模式(五)
- 單例模式-Singleton Pattern
- 確保對象的唯一性——單例模式 (一)
- 確保對象的唯一性——單例模式 (二)
- 確保對象的唯一性——單例模式 (三)
- 確保對象的唯一性——單例模式 (四)
- 確保對象的唯一性——單例模式 (五)
- 原型模式-Prototype Pattern
- 對象的克隆——原型模式(一)
- 對象的克隆——原型模式(二)
- 對象的克隆——原型模式(三)
- 對象的克隆——原型模式(四)
- 建造者模式-Builder Pattern
- 復雜對象的組裝與創建——建造者模式(一)
- 復雜對象的組裝與創建——建造者模式(二)
- 復雜對象的組裝與創建——建造者模式(三)
- 七個結構型模式
- 適配器模式-Adapter Pattern
- 不兼容結構的協調——適配器模式(一)
- 不兼容結構的協調——適配器模式(二)
- 不兼容結構的協調——適配器模式(三)
- 不兼容結構的協調——適配器模式(四)
- 橋接模式-Bridge Pattern
- 處理多維度變化——橋接模式(一)
- 處理多維度變化——橋接模式(二)
- 處理多維度變化——橋接模式(三)
- 處理多維度變化——橋接模式(四)
- 組合模式-Composite Pattern
- 樹形結構的處理——組合模式(一)
- 樹形結構的處理——組合模式(二)
- 樹形結構的處理——組合模式(三)
- 樹形結構的處理——組合模式(四)
- 樹形結構的處理——組合模式(五)
- 裝飾模式-Decorator Pattern
- 擴展系統功能——裝飾模式(一)
- 擴展系統功能——裝飾模式(二)
- 擴展系統功能——裝飾模式(三)
- 擴展系統功能——裝飾模式(四)
- 外觀模式-Facade Pattern
- 深入淺出外觀模式(一)
- 深入淺出外觀模式(二)
- 深入淺出外觀模式(三)
- 享元模式-Flyweight Pattern
- 實現對象的復用——享元模式(一)
- 實現對象的復用——享元模式(二)
- 實現對象的復用——享元模式(三)
- 實現對象的復用——享元模式(四)
- 實現對象的復用——享元模式(五)
- 代理模式-Proxy Pattern
- 設計模式之代理模式(一)
- 設計模式之代理模式(二)
- 設計模式之代理模式(三)
- 設計模式之代理模式(四)
- 十一個行為型模式
- 職責鏈模式-Chain of Responsibility Pattern
- 請求的鏈式處理——職責鏈模式(一)
- 請求的鏈式處理——職責鏈模式(二)
- 請求的鏈式處理——職責鏈模式(三)
- 請求的鏈式處理——職責鏈模式(四)
- 命令模式-Command Pattern
- 請求發送者與接收者解耦——命令模式(一)
- 請求發送者與接收者解耦——命令模式(二)
- 請求發送者與接收者解耦——命令模式(三)
- 請求發送者與接收者解耦——命令模式(四)
- 請求發送者與接收者解耦——命令模式(五)
- 請求發送者與接收者解耦——命令模式(六)
- 解釋器模式-Interpreter Pattern
- 自定義語言的實現——解釋器模式(一)
- 自定義語言的實現——解釋器模式(二)
- 自定義語言的實現——解釋器模式(三)
- 自定義語言的實現——解釋器模式(四)
- 自定義語言的實現——解釋器模式(五)
- 自定義語言的實現——解釋器模式(六)
- 迭代器模式-Iterator Pattern
- 遍歷聚合對象中的元素——迭代器模式(一)
- 遍歷聚合對象中的元素——迭代器模式(二)
- 遍歷聚合對象中的元素——迭代器模式(三)
- 遍歷聚合對象中的元素——迭代器模式(四)
- 遍歷聚合對象中的元素——迭代器模式(五)
- 遍歷聚合對象中的元素——迭代器模式(六)
- 中介者模式-Mediator Pattern
- 協調多個對象之間的交互——中介者模式(一)
- 協調多個對象之間的交互——中介者模式(二)
- 協調多個對象之間的交互——中介者模式(三)
- 協調多個對象之間的交互——中介者模式(四)
- 協調多個對象之間的交互——中介者模式(五)
- 備忘錄模式-Memento Pattern
- 撤銷功能的實現——備忘錄模式(一)
- 撤銷功能的實現——備忘錄模式(二)
- 撤銷功能的實現——備忘錄模式(三)
- 撤銷功能的實現——備忘錄模式(四)
- 撤銷功能的實現——備忘錄模式(五)
- 觀察者模式-Observer Pattern
- 對象間的聯動——觀察者模式(一)
- 對象間的聯動——觀察者模式(二)
- 對象間的聯動——觀察者模式(三)
- 對象間的聯動——觀察者模式(四)
- 對象間的聯動——觀察者模式(五)
- 對象間的聯動——觀察者模式(六)
- 狀態模式-State Pattern
- 處理對象的多種狀態及其相互轉換——狀態模式(一)
- 處理對象的多種狀態及其相互轉換——狀態模式(二)
- 處理對象的多種狀態及其相互轉換——狀態模式(三)
- 處理對象的多種狀態及其相互轉換——狀態模式(四)
- 處理對象的多種狀態及其相互轉換——狀態模式(五)
- 處理對象的多種狀態及其相互轉換——狀態模式(六)
- 策略模式-Strategy Pattern
- 算法的封裝與切換——策略模式(一)
- 算法的封裝與切換——策略模式(二)
- 算法的封裝與切換——策略模式(三)
- 算法的封裝與切換——策略模式(四)
- 模板方法模式-Template Method Pattern
- 模板方法模式深度解析(一)
- 模板方法模式深度解析(二)
- 模板方法模式深度解析(三)
- 訪問者模式-Visitor Pattern
- 操作復雜對象結構——訪問者模式(一)
- 操作復雜對象結構——訪問者模式(二)
- 操作復雜對象結構——訪問者模式(三)
- 操作復雜對象結構——訪問者模式(四)
- 設計模式趣味學習(復習)
- 設計模式與足球(一)
- 設計模式與足球(二)
- 設計模式與足球(三)
- 設計模式與足球(四)
- 設計模式綜合應用實例
- 多人聯機射擊游戲
- 多人聯機射擊游戲中的設計模式應用(一)
- 多人聯機射擊游戲中的設計模式應用(二)
- 數據庫同步系統
- 設計模式綜合實例分析之數據庫同步系統(一)
- 設計模式綜合實例分析之數據庫同步系統(二)
- 設計模式綜合實例分析之數據庫同步系統(三)