設(shè)計(jì)模式之訪問(wèn)者模式(Visitor Pattern)

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)陨溅。

VisitorPattern-UML

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)常被定義新的操作。

了解更多設(shè)計(jì)模式:

設(shè)計(jì)模式系列

參考資料:

http://c.biancheng.net/view/1397.html

https://www.runoob.com/design-pattern/visitor-pattern.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末兵怯,一起剝皮案震驚了整個(gè)濱河市彩匕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌媒区,老刑警劉巖推掸,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異驻仅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)登渣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門噪服,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人胜茧,你說(shuō)我怎么就攤上這事粘优。” “怎么了呻顽?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵雹顺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我廊遍,道長(zhǎng)嬉愧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任喉前,我火速辦了婚禮没酣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卵迂。我一直安慰自己裕便,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布见咒。 她就那樣靜靜地躺著偿衰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上下翎,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天缤言,我揣著相機(jī)與錄音,去河邊找鬼漏设。 笑死墨闲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的郑口。 我是一名探鬼主播鸳碧,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼犬性!你這毒婦竟也來(lái)了瞻离?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤乒裆,失蹤者是張志新(化名)和其女友劉穎套利,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹤耍,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肉迫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稿黄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喊衫。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖杆怕,靈堂內(nèi)的尸體忽然破棺而出族购,到底是詐尸還是另有隱情,我是刑警寧澤陵珍,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布寝杖,位于F島的核電站,受9級(jí)特大地震影響互纯,放射性物質(zhì)發(fā)生泄漏瑟幕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一伟姐、第九天 我趴在偏房一處隱蔽的房頂上張望收苏。 院中可真熱鬧,春花似錦愤兵、人聲如沸鹿霸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)懦鼠。三九已至钻哩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肛冶,已是汗流浹背街氢。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睦袖,地道東北人珊肃。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像馅笙,于是被迫代替她去往敵國(guó)和親伦乔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 【學(xué)習(xí)難度:★★☆☆☆董习,使用頻率:★★★☆☆】直接出處:訪問(wèn)者模式梳理和學(xué)習(xí):https://github.com...
    BruceOuyang閱讀 895評(píng)論 2 2
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是訪問(wèn)者模式 模式的結(jié)構(gòu) 典型代碼 訪問(wèn)者模式中的偽動(dòng)態(tài)雙分派 代碼示例 訪問(wèn)者...
    w1992wishes閱讀 865評(píng)論 0 6
  • 1 場(chǎng)景問(wèn)題# 1.1 擴(kuò)展客戶管理的功能## 考慮這樣一個(gè)應(yīng)用:擴(kuò)展客戶管理的功能烈和。 既然是擴(kuò)展功能,那么肯定是...
    七寸知架構(gòu)閱讀 2,923評(píng)論 1 58
  • 原圖是一夏飄雪的皿淋,畫出來(lái)沒有原圖顏色真招刹,應(yīng)該是紙和筆的原因,堅(jiān)持每天畫窝趣,為自己加油7枋睢!哑舒!
    韓信的畫閱讀 232評(píng)論 0 1
  • 基礎(chǔ) 讀程序,總結(jié)程序的功能: 1.求等比數(shù)列 2的20次方 2.計(jì)算在100以內(nèi)能夠被3和7整除缰儿,但不能同時(shí)被3...
    萌王史萊姆閱讀 265評(píng)論 0 0