文章目錄
- 前言
- 一、訪問者模式
- 二、訪問者模式示例
前言
GOF設計模式分三大類:
- 創建型模式:關注對象的創建過程,包括單例模式、簡單工廠模式、工廠方法模式、抽象工廠模式、原型模式和建造者模式。
- 結構型模式:關注類和對象之間的組合,包括適配器模式、橋接模式、組合模式、裝飾模式、外觀模式、享元模式和代理模式。
- 行為型模式:關注對象之間的交互,包括職責鏈模式、命令模式、解釋器模式、迭代器模式、中介者模式、備忘錄模式、觀察者模式、狀態模式、策略模式、模板方法模式和訪問者模式。
一、訪問者模式
訪問者模式(Visitor Pattern)
-
定義:提供一個作用于某對象結構中的各元素的操作表示,它使得可以在不改變各元素的類的前提下定義作用于這些元素的新操作。
-
解決問題:如何操作一個包含多種類型對象的復雜結構?
-
使用場景:
- 當系統中存在一個較為復雜的對象結構,且不同訪問者對其所采取的操作也不相同時,可以考慮使用訪問者模式進行設計。
- 一個對象結構包含多種類型的對象,希望對這些對象實施一些依賴其具體類型的操作。在訪問者中針對每一種具體的類型都提供了一個訪問操作,不同類型的對象可以有不同的訪問操作。
-
組成:
- Visitor(抽象訪問者):抽象訪問者為對象結構中每個具體元素類ConcreteElement聲明一個訪問操作,從這個操作的名稱或參數類型可以清楚知道需要訪問的具體元素的類型。
- ConcreteVisitor(具體訪問者):具體訪問者實現了每個由抽象訪問者聲明的操作,每個操作用于訪問對象結構中一種類型的元素。
- Element(抽象元素):定義一個accept()方法,該方法通常以一個抽象訪問者作為參數。
- ConcreteElement(具體元素):具體元素實現了accept()方法,在accept()方法中調用訪問者的訪問方法以便完成對一個元素的操作。
- ObjectStructure(對象結構):對象結構是一個元素的集合,它用于存放元素對象,并且提供了遍歷其內部元素的方法。
-
補充說明:
- 訪問者模式,包含訪問者和被訪問元素兩個主要組成部分。
- 這些被訪問的元素通常具有不同的類型,且不同的訪問者可以對它們進行不同的訪問操作。
- 被訪問的元素通常存儲在一個集合中,這個集合稱為“對象結構”。訪問者通過遍歷對象結構實現對其中存儲的元素的逐個操作。
- 訪問者模式包括兩個層次結構:一個是訪問者層次結構,提供了抽象訪問者和具體訪問者;另一個是元素層次結構,提供了抽象元素和具體元素。
-
優點:
- 增加新的訪問操作很方便。
- 將有關元素對象的訪問行為集中到一個訪問者對象中,而不是分散在一個個的元素類中。類的職責更加清晰。
-
缺點:
- 增加新的元素類很困難。
- 破壞封裝。
二、訪問者模式示例
使用訪問者模式,設計系統中員工數據匯總模塊
- 員工包括正式員工和臨時工,每周人力資源部和財務部等部門需要對員工數據進行匯總。人力資源部負責匯總每周員工工作時間,而財務部負責計算每周員工工資。
- 正式員工每周工作時間為40小時。不同級別、不同部門的員工每周基本工資不同。如果超過40小時,超出部分按照100元/小時作為加班費;如果少于40小時,所缺時間按照請假處理,請假所扣工資以80元/小時計算,直到基本工資扣除到零為止。
- 臨時工每周工作時間不固定。基本工資按小時計算,不同崗位的臨時工小時工資不同。
- FADepartment表示財務部,HRDepartment表示人力資源部,它們充當具體訪問者角色,其抽象父類Department充當抽象訪問者角色;
- EmployeeList充當對象結構,用于存儲員工列表;
- FulltimeEmployee表示正式員工,ParttimeEmployee表示臨時工,它們充當具體元素角色,其父接口Employee充當抽象元素角色。
"""訪問者模式"""### 抽象訪問者class Departement:"""部門"""def visit_fulltime_employee(self, employee: "FulltimeEmployee"):raise NotImplementedErrordef visit_parttime_employee(self, employee: "ParttimeEmployee"):raise NotImplementedError### 具體訪問者class Finance(Departement):"""財務部"""def visit_fulltime_employee(self, employee):"""實現財務部對全職員工的訪問"""if employee.work_time > 40:employee.weekly_wage = (employee.weekly_wage + (employee.work_time - 40) * 100)else:employee.weekly_wage = (employee.weekly_wage - (40 - employee.work_time) * 100)if employee.weekly_wage < 0:employee.weekly_wage = 0print(f"正式員工:{employee.name},實際工資為:{employee.weekly_wage}")def visit_parttime_employee(self, employee):"""實現人事部對兼職員工的訪問"""print(f"兼職員工:{employee.name},實際工資為:{employee.work_time*employee.hourly_wage}")class HR(Departement):"""人事部"""def visit_fulltime_employee(self, employee):"""實現人事部對全職員工的訪問"""print(f"正式員工:{employee.name},實際工作時間:{employee.work_time}")if employee.work_time > 40:print(f"正式員工:{employee.name},加班時間為:{employee.work_time - 40}")else:print(f"正式員工:{employee.name},請假時間為:{40-employee.work_time}")def visit_parttime_employee(self, employee):"""實現人事部對兼職員工的訪問"""print(f"兼職員工:{employee.name},實際工作時間:{employee.work_time}")### 抽象元素class Employee:"""員工"""def accept(self, handler: Departement):"""接受一個抽象訪問者訪問"""raise NotImplementedError### 具體元素class FulltimeEmployee(Employee):"""全職員工"""def __init__(self, name: str, weekly_wage: float, work_time: int):self.name = name # 員工姓名self.weekly_wage = weekly_wage # 員工周薪self.work_time = work_time # 工作時間def accept(self, handler):handler.visit_fulltime_employee(self) # 調用訪問者的訪問方法class ParttimeEmployee(Employee):"""兼職員工"""def __init__(self, name: str, hourly_wage: float, work_time: int):self.name = name # 員工姓名self.hourly_wage = hourly_wage # 員工時薪self.work_time = work_time # 工作時間def accept(self, handler):handler.visit_parttime_employee(self) # 調用訪問者的訪問方法### 對象結構class EmployeeList:"""員工列表"""def __init__(self):self.employees = []def add_employee(self, employee):self.employees.append(employee)def accept(self, departement):for employee in self.employees:employee.accept(departement)def __str__(self):return "\n".join([str(employee) for employee in self.employees])
- 客戶端代碼
### 客戶端代碼
if __name__ == "__main__":employee_list = EmployeeList()employee_list.add_employee(FulltimeEmployee("張三", 5000, 45))employee_list.add_employee(FulltimeEmployee("李四", 6000, 40))employee_list.add_employee(FulltimeEmployee("王五", 7000, 38))employee_list.add_employee(ParttimeEmployee("趙六", 80, 20))employee_list.add_employee(ParttimeEmployee("孫七", 60, 18))dep: Departement = Finance()# 如果需要更換具體訪問者類,無須修改源代碼,只需修改配置文件。# dep: Departement = HR()employee_list.accept(dep)
- 輸出結果
正式員工:張三,實際工資為:5500
正式員工:李四,實際工資為:6000
正式員工:王五,實際工資為:6800
兼職員工:趙六,實際工資為:1600
兼職員工:孫七,實際工資為:1080
- 當訪問者更換為HR時,輸出結果
正式員工:張三,實際工作時間:45
正式員工:張三,加班時間為:5
正式員工:李四,實際工作時間:40
正式員工:李四,請假時間為:0
正式員工:王五,實際工作時間:38
正式員工:王五,請假時間為:2
兼職員工:趙六,實際工作時間:20
兼職員工:孫七,實際工作時間:18
您正在閱讀的是《設計模式Python版》專欄!關注不迷路~