What:
提供一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作表示昧互,它使得可以在不改變各元素的類的前提下定義作用于這些元素的新操作挽铁。
Why:
優(yōu)點(diǎn):
1.各角色職責(zé)分離,符合單一職責(zé)原則敞掘。
2.擴(kuò)展性好叽掘,如果需要新增操作方式,只需要在具體訪問(wèn)者增加方法渐逃,靈活性高够掠。
3.使得數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作解耦,使得操作集合可以獨(dú)立變化茄菊。
缺點(diǎn):
1.具體元素對(duì)訪問(wèn)者公布細(xì)節(jié)疯潭,違反了迪米特原則赊堪。
2.增加新的元素類很困難,需要在每一個(gè)訪問(wèn)者類中增加相應(yīng)訪問(wèn)操作代碼竖哩,違背開閉原則哭廉。
3.訪問(wèn)者類visit方法參數(shù)為類對(duì)象,違背了依賴倒置轉(zhuǎn)原則相叁,應(yīng)該面向接口編程而不是類遵绰。
Where:
1.對(duì)象結(jié)構(gòu)中對(duì)象對(duì)應(yīng)的類很少改變,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作增淹。
2.需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作椿访,而需要避免讓這些操作"污染"這些對(duì)象的類,也不希望在增加新操作時(shí)修改這些類虑润。
How:
訪問(wèn)者模式有以下幾個(gè)角色:
抽象訪問(wèn)者(Visitor)角色: 定義一個(gè)訪問(wèn)具體元素的接口成玫,為每個(gè)具體元素類對(duì)應(yīng)一個(gè)訪問(wèn)操作 visit() ,該操作中的參數(shù)類型標(biāo)識(shí)了被訪問(wèn)的具體元素拳喻。
具體訪問(wèn)者(ConcreteVisitor)角色: 實(shí)現(xiàn)抽象訪問(wèn)者角色中聲明的各個(gè)訪問(wèn)操作哭当,確定訪問(wèn)者訪問(wèn)一個(gè)元素時(shí)該做什么。
抽象元素(Element)角色: 聲明一個(gè)包含接受操作 accept() 的接口冗澈,被接受的訪問(wèn)者對(duì)象作為 accept() 方法的參數(shù)钦勘。
具體元素(ConcreteElement)角色: 實(shí)現(xiàn)抽象元素角色提供的 accept() 操作,其方法體通常都是 visitor.visit(this) 亚亲,另外具體元素中可能還包含本身業(yè)務(wù)邏輯的相關(guān)操作彻采。
對(duì)象結(jié)構(gòu)(Object Structure)角色: 是一個(gè)包含元素角色的容器,提供讓訪問(wèn)者對(duì)象遍歷容器中的所有元素的方法朵栖,通常由 List颊亮、Set、Map 等聚合類實(shí)現(xiàn)陨溅。
Staff(抽象元素角色):
public interface Staff {
void accept(Department department);
}
Manager终惑、Engineer(具體元素角色):
public class Engineer implements Staff {
private String staffName;
private double workTime;
public Engineer(String staffName) {
this.staffName = staffName;
}
@Override
public void accept(Department department) {
department.visit(this);
}
//省略get、set方法
}
public class Manager implements Staff {
private String staffName;
private double workTime;
public Manager(String staffName) {
this.staffName = staffName;
}
@Override
public void accept(Department department) {
department.visit(this);
}
//省略get门扇、set方法
}
Department(抽象訪問(wèn)者角色):
public interface Department {
void visit(Engineer engineer);
void visit(Manager manager);
}
AccountingDepartment雹有、PersonnelDepartment(具體訪問(wèn)者角色):
public class AccountingDepartment implements Department{
private final double MANAGER_DAILY_SALARY = 1000D;
private final double ENGINEER_DAILY_SALARY = 500D;
@Override
public void visit(Engineer engineer) {
System.out.println(engineer.getStaffName() + " 工程師,月薪為:" + engineer.getWorkTime() * ENGINEER_DAILY_SALARY);
}
@Override
public void visit(Manager manager) {
System.out.println(manager.getStaffName() + " 經(jīng)理臼寄,月薪為:" + manager.getWorkTime() * MANAGER_DAILY_SALARY);
}
}
public class PersonnelDepartment implements Department {
@Override
public void visit(Engineer engineer) {
engineer.setWorkTime(workTime());
System.out.println(engineer.getStaffName() + " 工程師霸奕,上個(gè)月工作天數(shù)為:" + workTime());
}
@Override
public void visit(Manager manager) {
manager.setWorkTime(workTime());
System.out.println(manager.getStaffName() + " 經(jīng)理,上個(gè)月工作天數(shù)為:" + workTime());
}
//默認(rèn)所有人都工作22天
double workTime(){
return 22;
}
}
StaffContainer(對(duì)象結(jié)構(gòu)角色):
public class StaffContainer {
private List<Staff> list = new ArrayList<>();
public void addStaff(Staff staff){
list.add(staff);
}
public void accept(Department department){
Iterator<Staff> it = list.iterator();
while(it.hasNext()){
Staff staff = it.next();
staff.accept(department);
}
}
}
Test:測(cè)試類
public class Test {
public static void main(String[] args) {
Staff engineer = new Engineer("Marry");
Staff manager = new Manager("Tim");
Department personnelDepartment = new PersonnelDepartment();
Department accountingDepartment = new AccountingDepartment();
StaffContainer container = new StaffContainer();
container.addStaff(engineer);
container.addStaff(manager);
System.out.println("----------人事部統(tǒng)計(jì)員工上班天數(shù)----------");
container.accept(personnelDepartment);
System.out.println("----------財(cái)務(wù)部計(jì)算員工工資----------");
container.accept(accountingDepartment);
}
}
輸出結(jié)果:
----------人事部統(tǒng)計(jì)員工上班天數(shù)----------
Marry 工程師吉拳,上個(gè)月工作天數(shù)為:22.0
Tim 經(jīng)理质帅,上個(gè)月工作天數(shù)為:22.0
----------財(cái)務(wù)部計(jì)算員工工資----------
Marry 工程師,月薪為:11000.0
Tim 經(jīng)理,月薪為:22000.0
[圖片上傳失敗...(image-2eb971-1559461664640)]
總結(jié)
《設(shè)計(jì)模式》的作者評(píng)價(jià)為:大多情況下煤惩,你不需要使用訪問(wèn)者模式嫉嘀,但是一旦需要使用它時(shí),那就真的需要使用了魄揉。我們應(yīng)該慎用訪問(wèn)者模式剪侮,因?yàn)槭褂貌划?dāng)會(huì)導(dǎo)致系統(tǒng)復(fù)雜度增加,維護(hù)難度加大洛退。使用訪問(wèn)者模式的時(shí)候應(yīng)該考慮對(duì)象的結(jié)構(gòu)是否穩(wěn)定瓣俯,而對(duì)象結(jié)構(gòu)是否經(jīng)常被定義新的操作。