設(shè)計(jì)模式系列篇(十八)——訪問(wèn)者模式

What

訪問(wèn)者模式(Visitor Pattern)爆存,允許一個(gè)或者多個(gè)操作應(yīng)用到一組對(duì)象上株扛,解耦操作和對(duì)象本身。我們使用了一個(gè)訪問(wèn)者類(lèi)诉探,它改變了元素類(lèi)的執(zhí)行算法日熬。通過(guò)這種方式,元素的執(zhí)行算法可以隨著訪問(wèn)者改變而改變肾胯。這種類(lèi)型的設(shè)計(jì)模式屬于行為型模式竖席。根據(jù)模式,元素對(duì)象已接受訪問(wèn)者對(duì)象敬肚,這樣訪問(wèn)者對(duì)象就可以處理元素對(duì)象上的操作毕荐。

Why

  1. 符合單一職責(zé)原則。 訪問(wèn)者模式將操作和對(duì)象解耦艳馒,每個(gè)類(lèi)的職責(zé)非常單一憎亚。
  2. 優(yōu)秀的擴(kuò)展性。 如果想增加操作類(lèi)型鹰溜,無(wú)須對(duì)已有的穩(wěn)定的對(duì)象類(lèi)進(jìn)行更改虽填,具有很好的擴(kuò)展性。
  3. 靈活性曹动。優(yōu)秀擴(kuò)展性的必然結(jié)果,使得增加或刪除操作類(lèi)型都很方便牲览。

When

  1. 對(duì)象結(jié)構(gòu)中對(duì)象對(duì)應(yīng)的類(lèi)很少改變墓陈,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作。
  2. 需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作第献,而需要避免讓這些操作"污染"這些對(duì)象的類(lèi)贡必,也不希望在增加新操作時(shí)修改這些類(lèi)。

How

訪問(wèn)者模式的實(shí)現(xiàn)是比較難理解的庸毫,但訪問(wèn)者的實(shí)現(xiàn)大家不必掌握仔拟,只需要見(jiàn)到能認(rèn)出是訪問(wèn)者模式就好了。
下面飒赃,我們以實(shí)驗(yàn)室年底考核產(chǎn)生報(bào)表為例介紹訪問(wèn)者模式的實(shí)現(xiàn)利花。年底了,你的實(shí)驗(yàn)室迎來(lái)了一年一度的考核载佳,實(shí)驗(yàn)室參與年終考核的有學(xué)碩和專(zhuān)碩炒事,考核官包括導(dǎo)師和輔導(dǎo)員。對(duì)于導(dǎo)師來(lái)說(shuō)蔫慧,他關(guān)注的是學(xué)生的科研或者項(xiàng)目情況挠乳,而輔導(dǎo)員則更關(guān)注學(xué)生的課程成績(jī)和社會(huì)實(shí)踐。在這個(gè)例子中,學(xué)碩和專(zhuān)碩就是對(duì)象睡扬;而學(xué)生的科研盟蚣、項(xiàng)目情況就是操作,而訪問(wèn)者的類(lèi)型包括導(dǎo)師和輔導(dǎo)員卖怜。
UML圖如下所示:


訪問(wèn)者模式

包含以下幾部分:

  1. Visitor:接口或者抽象類(lèi)刁俭,定義了對(duì)每個(gè) Master子類(lèi) 訪問(wèn)的行為,它的參數(shù)就是被訪問(wèn)的元素韧涨,它的方法個(gè)數(shù)理論上與元素的個(gè)數(shù)是一樣的牍戚,因此,訪問(wèn)者模式要求元素的類(lèi)型要穩(wěn)定虑粥,如果經(jīng)常添加如孝、移除元素類(lèi),必然會(huì)導(dǎo)致頻繁地修改 Visitor 接口娩贷,如果出現(xiàn)這種情況第晰,則說(shuō)明不適合使用訪問(wèn)者模式。
  2. ConcreteVisitor:具體的訪問(wèn)者彬祖,如本例中的MentorVisitor茁瘦,它需要給出對(duì)每一個(gè)元素類(lèi)訪問(wèn)時(shí)所產(chǎn)生的具體行為。
  3. Master:元素接口或者抽象類(lèi)储笑,它定義了一個(gè)接受訪問(wèn)者(accept)的方法甜熔,其意義是指每一個(gè)元素都要可以被訪問(wèn)者訪問(wèn)。
  4. AcademicMaster突倍、EngineerMaster:具體的元素類(lèi)腔稀,它提供接受訪問(wèn)的具體實(shí)現(xiàn),而這個(gè)具體的實(shí)現(xiàn)羽历,通常情況下是使用訪問(wèn)者提供的訪問(wèn)該元素類(lèi)的方法焊虏。
    具體代碼如下:
    首先是Master及其子類(lèi)。
// 碩士抽象類(lèi)秕磷,定義基本屬性诵闭,并定義accept方法,參數(shù)為 Vistor 對(duì)象澎嚣。
public abstract class Master {
    private String name;
    private Double gpa;
    private Double socialPractice;
    private Integer paperCount;
    private Integer projectCount;

    public Master(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
        this.name = name;
        this.gpa = gpa;
        this.socialPractice = socialPractice;
        this.paperCount = paperCount;
        this.projectCount = projectCount;
    }

    // 省略getter方法
    public abstract void accept(Visitor visitor);
}

// 學(xué)碩類(lèi)
public class AcademicMaster extends Master {
    public AcademicMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
        super(name, gpa, socialPractice, paperCount, projectCount);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 專(zhuān)碩類(lèi)
public class EngineerMaster extends Master {
    public EngineerMaster(String name, Double gpa, Double socialPractice, Integer paperCount, Integer projectCount) {
        super(name, gpa, socialPractice, paperCount, projectCount);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

接下來(lái)是Visitor接口及其實(shí)現(xiàn)類(lèi)疏尿。

public interface Visitor {
    void visit(AcademicMaster academicMaster);

    void visit(EngineerMaster engineerMaster);
}

// 導(dǎo)師報(bào)表
public class MentorVisitor implements Visitor {
    @Override
    public void visit(AcademicMaster academicMaster) {
        System.out.println(String.format("name: %s, paper count: %d",
                academicMaster.getName(), academicMaster.getPaperCount()));
    }

    @Override
    public void visit(EngineerMaster engineerMaster) {
        System.out.println(String.format("name: %s, project count: %d",
                engineerMaster.getName(), engineerMaster.getProjectCount()));
    }
}

// 輔導(dǎo)員報(bào)表
public class CounselorVisitor implements Visitor {
    @Override
    public void visit(AcademicMaster academicMaster) {
        System.out.println(String.format("name: %s, gpa: %f",
                academicMaster.getName(), academicMaster.getGpa()));
    }

    @Override
    public void visit(EngineerMaster engineerMaster) {
        System.out.println(String.format("name: %s, social practice: %f",
                engineerMaster.getName(), engineerMaster.getSocialPractice()));
    }
}

通過(guò)上面兩部分代碼,大家可以看出币叹,對(duì)Master類(lèi)對(duì)象的操作都集中在Visitor實(shí)現(xiàn)中润歉,這就實(shí)現(xiàn)了對(duì)象元素和操作解耦。如果增加訪問(wèn)者颈抚,如家長(zhǎng)或者院領(lǐng)導(dǎo)都無(wú)須修改Master類(lèi)踩衩,只需要增加相應(yīng)的訪問(wèn)者實(shí)現(xiàn)就可以了嚼鹉。

最后,給個(gè)測(cè)試類(lèi)驱富。

public class TestMain {
    public static void main(String[] args) {
        Master academicMaster1 = new AcademicMaster("Jeremy", 3.7, 3.0, 1, 3);
        Master academicMaster2 = new AcademicMaster("Bob", 3.2, 2.0, 2, 1);
        Master engineerMaster1 = new EngineerMaster("Tom", 3.3, 4.0, 0, 1);
        Master engineerMaster2 = new EngineerMaster("Amy", 3.0, 3.0, 1, 2);
        List<Master> masters = new ArrayList<>();
        masters.add(academicMaster1);
        masters.add(academicMaster2);
        masters.add(engineerMaster1);
        masters.add(engineerMaster2);

        System.out.println("-----mentor's report-----");
        Visitor mentorVisitor = new MentorVisitor();
        for (Master master : masters) {
            master.accept(mentorVisitor);
        }

        System.out.println("-----counselor's report-----");
        Visitor counselorVisitor = new CounselorVisitor();
        for (Master master : masters) {
            master.accept(counselorVisitor);
        }
    }
}

輸出如下:

-----mentor's report-----
name: Jeremy, paper count: 1
name: Bob, paper count: 2
name: Tom, project count: 1
name: Amy, project count: 2
-----counselor's report-----
name: Jeremy, gpa: 3.700000
name: Bob, gpa: 3.200000
name: Tom, social practice: 4.000000
name: Amy, social practice: 3.000000

搞定锚赤。

代碼地址

i-learning

寫(xiě)在最后

如果你覺(jué)得我寫(xiě)的文章幫到了你,歡迎點(diǎn)贊褐鸥、評(píng)論线脚、分享、贊賞哦叫榕,你們的鼓勵(lì)是我不斷創(chuàng)作的動(dòng)力~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浑侥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晰绎,更是在濱河造成了極大的恐慌寓落,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荞下,死亡現(xiàn)場(chǎng)離奇詭異伶选,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)尖昏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)仰税,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人抽诉,你說(shuō)我怎么就攤上這事陨簇。” “怎么了掸鹅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵塞帐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我巍沙,道長(zhǎng),這世上最難降的妖魔是什么荷鼠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任句携,我火速辦了婚禮,結(jié)果婚禮上允乐,老公的妹妹穿的比我還像新娘矮嫉。我一直安慰自己,他們只是感情好牍疏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布蠢笋。 她就那樣靜靜地躺著,像睡著了一般鳞陨。 火紅的嫁衣襯著肌膚如雪昨寞。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音援岩,去河邊找鬼歼狼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛享怀,可吹牛的內(nèi)容都是我干的羽峰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼添瓷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼梅屉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鳞贷,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坯汤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后悄晃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體玫霎,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年妈橄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了庶近。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眷蚓,死狀恐怖鼻种,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沙热,我是刑警寧澤叉钥,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站篙贸,受9級(jí)特大地震影響投队,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜爵川,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一敷鸦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寝贡,春花似錦扒披、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至颇蜡,卻和暖如春价说,著一層夾襖步出監(jiān)牢的瞬間辆亏,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工熔任, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留褒链,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓疑苔,卻偏偏與公主長(zhǎng)得像甫匹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惦费,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359