設計模式的藝術 行為型模式之訪問者模式
前言
在公司上班,一般會有兼職或全職的員工,他們都發工資,上同樣的班,但是工資待遇是有區別的,財務部和人事部過來調查處理的手法也不是一樣的,雖然都是一樣的計算工資待遇,在軟體開發中存在著這樣的一種情況,我們需要處理著像員工一樣的集合,集合中的具體物件是不一樣的,去訪問時處理的手段也不一樣,軟體設計模式中有這麼一樣設計模式可以滿足上述要求,其動機就是以不同的方式去操作複雜物件結構
什麼是訪問者模式 Visitor Pattern
提供一個作用於某物件結構中的各元素的操作表示,它使得可以在不改變各元素結構的前提下定義作用於這些元素的新操作,訪問者模式是一種物件行為型模式
訪問者模式的優點
(1)、增加新的訪問操作方便,使用訪問者模式,增加新的訪問操作就意味著增加一個新的具體訪問者類,實現簡單,無須修改程式碼,符合開閉原則
(2)、將有關元素物件的訪問行為集中到一個訪問者物件中,而不是分散在一個個的元素類中,類的職責更加清晰,有利於物件結構中元素物件的複用,相同的物件結構可以提供不同的訪問者訪問
(3)、讓使用者可以不修改現有的元素類層次結構的情況下,定義作用於該層次的結構操作
訪問者模式的缺點
(1)、增加新的元素類很困難,在訪問者模式中,每增加一個新的元素類都意味著在抽象訪問者角色中增加一個新的抽象操作,並在每一個具體訪問者中都將增加相應的具體操作,這就違背了開閉原則
(2)、破壞了封裝,訪問者模式要求訪問者物件並訪問並呼叫每一個元素物件的操作,這就意味著元素物件有時候必須暴露一些自己的內部操作和內部狀態,否則無法供訪問者訪問
訪問者模式的使用場景
(1)、當一個物件結構包含著多個型別物件,希望對這些物件實施一些依賴其具體型別的操作。在訪問者中針對每一種具體的型別都提供了一個訪問操作,不同型別的物件可以有不同的訪問操作
(2)、需要對一個物件結構中的物件進行很多不同的操作並且不相關的操作,而且需要避免讓這些操作汙染這些物件的類,也不希望在增加新操作時修改這些類,訪問者模式將相關的訪問操作集中起來定義在訪問者類中,物件結構可以被不同的訪問者類所使用,將物件本身的訪問操作分離
(3)、物件結構中物件對應的類很少改變,但經常需要在此物件結構上定義新的操作
訪問者模式的具體實現
目錄結構
員工抽象類
//員工類:抽象元素類
public interface Employee {
public void accept(Department handler); //接受一個抽象訪問者訪問
}
具體員工類
//全職員工類:具體元素類
public class FulltimeEmployee implements Employee {
private String name; //員工姓名
private double weeklyWage; //員工週薪
private int weekTime; //工作時間
@Override
public void accept(Department handler) {
handler.visit(this); //呼叫訪問者的訪問方法
}
public FulltimeEmployee(String name, double weeklyWage, int weekTime) {
this.name = name;
this.weeklyWage = weeklyWage;
this.weekTime = weekTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeeklyWage() {
return weeklyWage;
}
public void setWeeklyWage(double weeklyWage) {
this.weeklyWage = weeklyWage;
}
public int getWeekTime() {
return weekTime;
}
public void setWeekTime(int weekTime) {
this.weekTime = weekTime;
}
}
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHourWage() {
return hourWage;
}
public void setHourWage(double hourWage) {
this.hourWage = hourWage;
}
public int getWorkTime() {
return workTime;
}
public void setWorkTime(int workTime) {
this.workTime = workTime;
}
@Override
public void accept(Department handler) {
handler.visit(this); //呼叫訪問者訪問的方法
}
}
抽象部門類
//部門類:抽象訪問者類,
public abstract class Department {
//宣告一組過載的訪問方法,用於訪問不同型別的具體元素
public abstract void visit(FulltimeEmployee employee);
public abstract void visit(ParttimeEmployee employee);
}
具體訪問者類
//財務部類:具體訪問者類
public class FADepartment extends Department {
@Override
//實現財務部對全職員工的訪問
public void visit(FulltimeEmployee employee) {
int workTime=employee.getWeekTime();
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()+"實際工資為"+weekWage+"元");
}
@Override
//實現財務部對兼職員工的訪問
public void visit(ParttimeEmployee employee) {
int workTime=employee.getWorkTime();
double hourWage=employee.getHourWage();
System.out.println("臨時工"+employee.getName()+"實際工資為"+workTime*hourWage+"元");
}
}
//人力資源部類:具體訪問者類
public class HRDepartment extends Department {
//實現人力資源部對全員的訪問
@Override
public void visit(FulltimeEmployee employee) {
int workTime=employee.getWeekTime();
System.out.println("正式員工"+employee.getName()+"實際工作時間為"+workTime+"小時");
if(workTime>40){
System.out.println("正式員工"+employee.getName()+"加班時間為"+(workTime-40)+"小時");
}else if(workTime<40){
System.out.println("正式員工"+employee.getName()+"請假時間為"+(40-workTime)+"小時");
}
}
//實現人力資源對兼職員工的訪問
@Override
public void visit(ParttimeEmployee employee) {
int workTime=employee.getWorkTime();
System.out.println("臨時工"+employee.getName()+"實際工作時間為"+workTime+"小時");
}
}
員工列表類
//員工列表類:物件結構
public class EmployeeList {
//定義了一個集合用於儲存員工物件
private ArrayList<Employee> list=new ArrayList<Employee>();
public void addEmployee(Employee employee){
list.add(employee);
}
//遍歷訪問員工集合中的每一個員工物件
public void accept(Department handler){
for(Object object:list){
((Employee)object).accept(handler);
}
}
}
輔助工具類
public class XMLUtil {
//該方法用於從XML配置檔案中提取具體類類名,並返回一個例項物件
public static Object getBean() {
try {
//建立文件物件
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(XMLUtil.class.getClassLoader().getResource("").getPath() + new File("cfg.xml"));
//獲取包含類名的文字節點
NodeList list = document.getElementsByTagName("className");
Node node = list.item(0).getFirstChild();
String className = node.getNodeValue();
//通過類名生成例項物件並將其返回
Class c = Class.forName(className);
Object object = c.newInstance();
return object;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
配置檔案
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<className>com.company.FADepartment</className>
</config>
客戶端測試類
public class Main {
public static void main(String[] args) {
EmployeeList list = new EmployeeList();
Employee fte1, fte2, fte3, pte1, pte2;
fte1 = new FulltimeEmployee("張無忌", 3200.00, 23);
fte2 = new FulltimeEmployee("楊過", 234213.3, 324);
fte3 = new FulltimeEmployee("段譽", 2400, 23);
pte1 = new FulltimeEmployee("洪七公", 80, 20);
pte2 = new FulltimeEmployee("郭靖", 60.0, 43);
list.addEmployee(fte1);
list.addEmployee(fte2);
list.addEmployee(fte3);
list.addEmployee(pte1);
list.addEmployee(pte2);
Department dep;
dep=(Department) XMLUtil.getBean();
list.accept(dep);
}
}
轉載請註明出處,掌聲送給社會人