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

What

訪問者模式(Visitor Pattern),允許一個或者多個操作應(yīng)用到一組對象上,解耦操作和對象本身。我們使用了一個訪問者類,它改變了元素類的執(zhí)行算法。通過這種方式,元素的執(zhí)行算法可以隨著訪問者改變而改變。這種類型的設(shè)計模式屬于行為型模式。根據(jù)模式,元素對象已接受訪問者對象,這樣訪問者對象就可以處理元素對象上的操作。

Why

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

When

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

How

訪問者模式的實(shí)現(xiàn)是比較難理解的,但訪問者的實(shí)現(xiàn)大家不必掌握,只需要見到能認(rèn)出是訪問者模式就好了。
下面,我們以實(shí)驗(yàn)室年底考核產(chǎn)生報表為例介紹訪問者模式的實(shí)現(xiàn)。年底了,你的實(shí)驗(yàn)室迎來了一年一度的考核,實(shí)驗(yàn)室參與年終考核的有學(xué)碩和專碩,考核官包括導(dǎo)師和輔導(dǎo)員。對于導(dǎo)師來說,他關(guān)注的是學(xué)生的科研或者項(xiàng)目情況,而輔導(dǎo)員則更關(guān)注學(xué)生的課程成績和社會實(shí)踐。在這個例子中,學(xué)碩和專碩就是對象;而學(xué)生的科研、項(xiàng)目情況就是操作,而訪問者的類型包括導(dǎo)師和輔導(dǎo)員。
UML圖如下所示:


訪問者模式

包含以下幾部分:

  1. Visitor:接口或者抽象類,定義了對每個 Master子類 訪問的行為,它的參數(shù)就是被訪問的元素,它的方法個數(shù)理論上與元素的個數(shù)是一樣的,因此,訪問者模式要求元素的類型要穩(wěn)定,如果經(jīng)常添加、移除元素類,必然會導(dǎo)致頻繁地修改 Visitor 接口,如果出現(xiàn)這種情況,則說明不適合使用訪問者模式。
  2. ConcreteVisitor:具體的訪問者,如本例中的MentorVisitor,它需要給出對每一個元素類訪問時所產(chǎn)生的具體行為。
  3. Master:元素接口或者抽象類,它定義了一個接受訪問者(accept)的方法,其意義是指每一個元素都要可以被訪問者訪問。
  4. AcademicMaster、EngineerMaster:具體的元素類,它提供接受訪問的具體實(shí)現(xiàn),而這個具體的實(shí)現(xiàn),通常情況下是使用訪問者提供的訪問該元素類的方法。
    具體代碼如下:
    首先是Master及其子類。
// 碩士抽象類,定義基本屬性,并定義accept方法,參數(shù)為 Vistor 對象。
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é)碩類
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);
    }
}

// 專碩類
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);
    }
}

接下來是Visitor接口及其實(shí)現(xiàn)類。

public interface Visitor {
    void visit(AcademicMaster academicMaster);

    void visit(EngineerMaster engineerMaster);
}

// 導(dǎ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)員報表
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()));
    }
}

通過上面兩部分代碼,大家可以看出,對Master類對象的操作都集中在Visitor實(shí)現(xiàn)中,這就實(shí)現(xiàn)了對象元素和操作解耦。如果增加訪問者,如家長或者院領(lǐng)導(dǎo)都無須修改Master類,只需要增加相應(yīng)的訪問者實(shí)現(xiàn)就可以了。

最后,給個測試類。

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

寫在最后

如果你覺得我寫的文章幫到了你,歡迎點(diǎn)贊、評論、分享、贊賞哦,你們的鼓勵是我不斷創(chuàng)作的動力~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容