一忧风、概述
將作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作分離出來(lái)封裝成獨(dú)立的類(lèi)布轿,使其在不改變數(shù)據(jù)結(jié)構(gòu)的前提下可以添加作用于這些元素的新的操作甫煞,為數(shù)據(jù)結(jié)構(gòu)中的每個(gè)元素提供多種訪問(wèn)方式掺冠。它將對(duì)數(shù)據(jù)的操作與數(shù)據(jù)結(jié)構(gòu)進(jìn)行分離沉馆,是行為類(lèi)模式中最復(fù)雜的一種模式。
主要角色:
抽象訪問(wèn)者角色:定義一個(gè)訪問(wèn)具體元素的接口德崭,為每個(gè)具體元素類(lèi)對(duì)應(yīng)一個(gè)訪問(wèn)操作 visit() 斥黑,該操作中的參數(shù)類(lèi)型標(biāo)識(shí)了被訪問(wèn)的具體元素。
具體訪問(wèn)者角色:實(shí)現(xiàn)抽象訪問(wèn)者角色中聲明的各個(gè)訪問(wèn)操作眉厨,確定訪問(wèn)者訪問(wèn)一個(gè)元素時(shí)該做什么锌奴。
抽象元素角色:聲明一個(gè)包含接受操作 accept() 的接口,被接受的訪問(wèn)者對(duì)象作為 accept() 方法的參數(shù)憾股。
具體元素角色:實(shí)現(xiàn)抽象元素角色提供的 accept() 操作鹿蜀,其方法體通常都是 visitor.visit(this) 箕慧,另外具體元素中可能還包含本身業(yè)務(wù)邏輯的相關(guān)操作。
對(duì)象結(jié)構(gòu)角色:是一個(gè)包含元素角色的容器茴恰,提供讓訪問(wèn)者對(duì)象遍歷容器中的所有元素的方法销钝,通常由 List、Set琐簇、Map 等聚合類(lèi)實(shí)現(xiàn)蒸健。
二、代碼實(shí)現(xiàn)
舉例:比如我的朋友們來(lái)訪問(wèn)我家婉商,我家有很多的房間似忧,每個(gè)朋友都會(huì)訪問(wèn)我不同的房間,角色分類(lèi)如下:
具體訪問(wèn)者角色:我的朋友們
具體元素角色:房間
對(duì)象結(jié)構(gòu)角色:我家
我家中包含很多房間丈秩,就是對(duì)象結(jié)構(gòu)角色包含元素角色盯捌。接下來(lái)看代碼實(shí)現(xiàn):
抽象訪問(wèn)者角色:朋友
public interface Friend {
//訪問(wèn)房間A
void visit(RoomA roomA);
//訪問(wèn)房間B
void visit(RoomB roomB);
}
具體訪問(wèn)者角色:張三
public class ZhangSan implements Friend{
@Override
public void visit(RoomA roomA) {
System.out.println("張三訪問(wèn)房間A");
}
@Override
public void visit(RoomB roomB) {
System.out.println("張三訪問(wèn)房間B");
}
}
具體訪問(wèn)者角色:李四
public class LiSi implements Friend{
@Override
public void visit(RoomA roomA) {
System.out.println("李四訪問(wèn)房間A");
}
@Override
public void visit(RoomB roomB) {
System.out.println("李四訪問(wèn)房間B");
}
}
抽象元素角色:房間
public interface Room {
//接收訪問(wèn)
void accept(Friend friend);
}
具體元素角色:房間A
public class RoomA implements Room{
@Override
public void accept(Friend friend) {
friend.visit(this);
System.out.println("訪問(wèn)了房間A");
}
}
具體元素角色:房間B
public class RoomB implements Room{
@Override
public void accept(Friend friend) {
friend.visit(this);
System.out.println("訪問(wèn)了房間B");
}
}
對(duì)象結(jié)構(gòu)角色:房子
public class Home {
//房子中包含房間
private List<Room> list = new ArrayList<>();
public void addRoom(Room room){
list.add(room);
}
public void action(Friend friend){
for (Room room:list){
room.accept(friend);
}
}
}
使用:
public static void main(String[] args) {
//創(chuàng)建房子
Home home = new Home();
//添加房間
home.addRoom(new RoomA());
home.addRoom(new RoomB());
//張三
ZhangSan zhangsan = new ZhangSan();
//張三訪問(wèn)房間
home.action(zhangsan);
//李四
LiSi lisi = new LiSi();
home.action(lisi);
}
輸出:
張三訪問(wèn)房間A
張三訪問(wèn)房間B
李四訪問(wèn)房間A
李四訪問(wèn)房間B
三、總結(jié)
優(yōu)點(diǎn):
- 擴(kuò)展性好蘑秽。能夠在不修改對(duì)象結(jié)構(gòu)中的元素的情況下饺著,為對(duì)象結(jié)構(gòu)中的元素添加新的功能。
- 復(fù)用性好肠牲∮姿ィ可以通過(guò)訪問(wèn)者來(lái)定義整個(gè)對(duì)象結(jié)構(gòu)通用的功能,從而提高系統(tǒng)的復(fù)用程度缀雳。
- 靈活性好渡嚣。訪問(wèn)者模式將數(shù)據(jù)結(jié)構(gòu)與作用于結(jié)構(gòu)上的操作解耦,使得操作集合可相對(duì)自由地演化而不影響系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)肥印。
- 符合單一職責(zé)原則识椰。訪問(wèn)者模式把相關(guān)的行為封裝在一起,構(gòu)成一個(gè)訪問(wèn)者深碱,使每一個(gè)訪問(wèn)者的功能都比較單一腹鹉。
缺點(diǎn):
- 增加新的元素類(lèi)很困難。在訪問(wèn)者模式中敷硅,每增加一個(gè)新的元素類(lèi)功咒,都要在每一個(gè)具體訪問(wèn)者類(lèi)中增加相應(yīng)的具體操作,這違背了“開(kāi)閉原則”竞膳。
- 破壞封裝航瞭。訪問(wèn)者模式中具體元素對(duì)訪問(wèn)者公布細(xì)節(jié)诫硕,這破壞了對(duì)象的封裝性坦辟。
- 違反了依賴(lài)倒置原則。訪問(wèn)者模式依賴(lài)了具體類(lèi)章办,而沒(méi)有依賴(lài)抽象類(lèi)锉走。
使用場(chǎng)景:
- 對(duì)象結(jié)構(gòu)相對(duì)穩(wěn)定滨彻,但其操作算法經(jīng)常變化的程序。
- 對(duì)象結(jié)構(gòu)中的對(duì)象需要提供多種不同且不相關(guān)的操作挪蹭,而且要避免讓這些操作的變化影響對(duì)象的結(jié)構(gòu)亭饵。