最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java設(shè)計(jì)模式之訪問者模式

 更新時(shí)間:2022年10月21日 08:41:21   作者:tianClassmate  
這篇文章介紹了Java設(shè)計(jì)模式之訪問者模式,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

大多數(shù)情況下你不需要訪問者模式,但當(dāng)一旦需要訪問者模式時(shí),那就是真的需要它了,這是設(shè)計(jì)模式創(chuàng)始人的原話。可以看出應(yīng)用場(chǎng)景比較少,但需要它的時(shí)候是不可或缺的,這篇文章就開始學(xué)習(xí)最后一個(gè)設(shè)計(jì)模式——訪問者模式。

一、概念理解

訪問者模式概念:封裝作用于某對(duì)象結(jié)構(gòu)中的各元素的操作,它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。

通俗的解釋就是,系統(tǒng)中有一些固定結(jié)構(gòu)的對(duì)象(元素),在其內(nèi)部提供一個(gè)accept()方法用來接受訪問者對(duì)象的訪問,不同的訪問者對(duì)同一元素的訪問內(nèi)容不同,所以使得相同的元素可以產(chǎn)生不同的元素結(jié)果。

比如在一個(gè)人事管理系統(tǒng)中,有多個(gè)工種的員工和多個(gè)老板,不同的老板對(duì)同一個(gè)員工的關(guān)注點(diǎn)是不同的,CTO可能關(guān)注的就是技術(shù),CEO可能更注重績(jī)效。

員工就是一個(gè)穩(wěn)定的元素,老板就是變化的,對(duì)應(yīng)概念就是:封裝員工的一些操作,可以在不改變員工類的前提下,增加新的老板訪問同一個(gè)員工。

在訪問者模式中包含五個(gè)角色,抽象元素、具體元素、抽象訪問者、具體訪問者、結(jié)構(gòu)元素。

抽象元素:定義一個(gè)接受訪問的方法accept,參數(shù)為訪問者對(duì)象。

具體元素:提供接受訪問者訪問的具體實(shí)現(xiàn)調(diào)用訪問者的訪問visit,并定義額外的數(shù)據(jù)操作方法。

抽象訪問者:這個(gè)角色主要是定義對(duì)具體元素的訪問方法visit,理論上來說方法數(shù)等于元素(固定類型的對(duì)象,也就是被訪問者)個(gè)數(shù)。

具體訪問者:實(shí)現(xiàn)對(duì)具體元素的訪問visit方法,參數(shù)就是具體元素。

結(jié)構(gòu)對(duì)象:創(chuàng)建一個(gè)數(shù)組用來維護(hù)元素,并提供一個(gè)方法訪問所有的元素。

二、案例實(shí)現(xiàn)

在一個(gè)公司有干活的工程師和管理者,也有抓技術(shù)的CTO和管績(jī)效的CEO,CTO和CEO都會(huì)訪問管理員和工程師,當(dāng)公司來了新的老板,只需要增加訪問者即可。

工程師和管理者就是元素、公司就是結(jié)構(gòu)體、CEO、CTO就是訪問者。

抽象元素:

/**
 *  員工 抽象元素 被訪問者
 * @author tcy
 * @Date 29-09-2022
 */
public interface ElementAbstract {
    void accept(VisitorAbstract visitor);
}

具體元素-工程師:

/**
 * 工程師 具體元素 被訪問者
 * @author tcy
 * @Date 29-09-2022
 */
public class ElementEngineer implements ElementAbstract {

    private String name;

    private int kpi;

    ElementEngineer(String name){
        this.name = name;
        this.kpi = new Random().nextInt(10);
    }

    public String getName() {
        return name;
    }

    public int getKpi() {
        return kpi;
    }


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

    public int getCodeLineTotal(){
        return this.kpi * 1000000;
    }
}

具體元素-管理者:

/**
 * 管理者 具體元素 被訪問者
 * @author tcy
 * @Date 29-09-2022
 */
public class ElementManager implements ElementAbstract {
    private String name;

    private int kpi;

    ElementManager(String name){
        this.name = name;
        this.kpi = new Random().nextInt(10);
    }

    public String getName() {
        return name;
    }

    public int getKpi() {
        return kpi;
    }

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

    public int getProductNum(){
        return this.kpi * 10;
    }

}

抽象訪問者:

/**
 * 抽象訪問者
 * @author tcy
 * @Date 29-09-2022
 */
public interface VisitorAbstract {
    void visit(ElementEngineer engineer);

    void visit(ElementManager manager);
}

具體訪問者-CEO

/**
 * 具體訪問者CEO
 * @author tcy
 * @Date 29-09-2022
 */
public class VisitorCEO implements VisitorAbstract {
    @Override
    public void visit(ElementEngineer engineer) {
        System.out.println("工程師:" + engineer.getName() + "KPI:" + engineer.getKpi());
    }

    @Override
    public void visit(ElementManager manager) {
        System.out.println("經(jīng)理:" + manager.getName() + "KPI:" + manager.getKpi() + " 今年共完成項(xiàng)目:" + manager.getProductNum() + "個(gè)");
    }
}

具體訪問者-CTO

/**
 * 具體訪問者CTO
 * @author tcy
 * @Date 29-09-2022
 */
public class VisitorCTO implements VisitorAbstract {
    @Override
    public void visit(ElementEngineer engineer) {
        System.out.println("工程師:" + engineer.getName() + " 今年代碼量" + engineer.getCodeLineTotal() + "行");
    }

    @Override
    public void visit(ElementManager manager) {
        System.out.println("經(jīng)理:" + manager.getName() + " 今年共完成項(xiàng)目:" + manager.getProductNum() + "個(gè)");
    }
}

結(jié)構(gòu)體:

/**
 * 結(jié)構(gòu)對(duì)象
 * @author tcy
 * @Date 29-09-2022
 */
public class Structure {
    List<ElementAbstract> list = new ArrayList<>();

    public Structure addEmployee(ElementAbstract employee){
        list.add(employee);
        return this;
    }

    public void report(VisitorAbstract visitor){
        list.forEach(employee -> {
            employee.accept(visitor);
        });
    }

}

客戶端:

/**
 * @author tcy
 * @Date 29-09-2022
 */
public class Client {
    public static void main(String[] args) {
        //元素對(duì)象
        ElementEngineer engineerZ = new ElementEngineer("小張");
        ElementEngineer engineerW = new ElementEngineer("小王");
        ElementEngineer engineerL = new ElementEngineer("小李");

        ElementManager managerZ = new ElementManager("張總");
        ElementManager managerW = new ElementManager("王總");
        ElementManager managerL = new ElementManager("李總");

        //結(jié)構(gòu)體對(duì)象
        Structure structure = new Structure();
        structure.addEmployee(engineerZ).addEmployee(engineerW).addEmployee(engineerL).addEmployee(managerZ).addEmployee(managerW).addEmployee(managerL);
        structure.report(new VisitorCTO());
        System.out.println("---------------------------------------");
        structure.report(new VisitorCEO());


    }
}

訪問者不愧是最難的設(shè)計(jì)模式,方法間的調(diào)用錯(cuò)綜復(fù)雜,日常開發(fā)的使用頻率很低,很多程序員寧可代碼寫的麻煩一點(diǎn)也不用這種設(shè)計(jì)模式,但是作為學(xué)習(xí)者就要學(xué)習(xí)各種設(shè)計(jì)模式了。

三、訪問者模式在JDk中的應(yīng)用

JDK的NIO中的 FileVisitor 接口采用的就是訪問者模式。

在早期的 Java 版本中,如果要對(duì)指定目錄下的文件進(jìn)行遍歷,必須用遞歸的方式來實(shí)現(xiàn),這種方法復(fù)雜且靈活性不高。

Java 7 版本后,F(xiàn)iles 類提供了 walkFileTree() 方法,該方法可以很容易的對(duì)目錄下的所有文件進(jìn)行遍歷,需要 Path、FileVisitor 兩個(gè)參數(shù)。其中,Path 是要遍歷文件的路徑,F(xiàn)ileVisitor 則可以看成一個(gè)文件訪問器,源碼如下。

FileVisitor 主要提供了 4 個(gè)方法,且返回結(jié)果的都是 FileVisitResult 對(duì)象值,用于決定當(dāng)前操作完成后接下來該如何處理。FileVisitResult 是一個(gè)枚舉類,代表返回之后的一些后續(xù)操作,源碼如下。

FileVisitResult 主要包含 4 個(gè)常見的操作。

  • FileVisitResult.CONTINUE:這個(gè)訪問結(jié)果表示當(dāng)前的遍歷過程將會(huì)繼續(xù)。
  • FileVisitResult.SKIP_SIBLINGS:這個(gè)訪問結(jié)果表示當(dāng)前的遍歷過程將會(huì)繼續(xù),但是要忽略當(dāng)前文件/目錄的兄弟節(jié)點(diǎn)。
  • FileVisitResult.SKIP_SUBTREE:這個(gè)訪問結(jié)果表示當(dāng)前的遍歷過程將會(huì)繼續(xù),但是要忽略當(dāng)前目錄下的所有節(jié)點(diǎn)。
  • FileVisitResult.TERMINATE:這個(gè)訪問結(jié)果表示當(dāng)前的遍歷過程將會(huì)停止。

通過訪問者去遍歷文件樹會(huì)比較方便,比如查找文件夾內(nèi)符合某個(gè)條件的文件或者某一天內(nèi)所創(chuàng)建的文件,這個(gè)類中都提供了相對(duì)應(yīng)的方法。它的實(shí)現(xiàn)也非常簡(jiǎn)單,代碼如下。

在JDK的應(yīng)用中我們提供的文件就看做是一個(gè)穩(wěn)定元素,對(duì)應(yīng)訪問者模式中的抽象元素;而Files.walkFileTree()方法中的FileVisitor 參數(shù)就可看做是角色中的訪問者。

四、訪問者模式中的偽動(dòng)態(tài)雙分派

訪問者模式中有一個(gè)重要的概念叫:偽動(dòng)態(tài)雙分派。

我們一步一步解讀它的含義,什么叫分派?根據(jù)對(duì)象的類型而對(duì)方法進(jìn)行的選擇,就是分派(Dispatch)。

發(fā)生在編譯時(shí)的分派叫靜態(tài)分派,例如重載(overload),發(fā)生在運(yùn)行時(shí)的分派叫動(dòng)態(tài)分派,例如重寫(overwrite)。

其中分派又分為單分派和多分派。

單分派:依據(jù)單個(gè)變量進(jìn)行方法的選擇就叫單分派,Java 動(dòng)態(tài)分派(重寫)只根據(jù)方法的接收者一個(gè)變量進(jìn)行分配,所以其是單分派。

多分派:依據(jù)多個(gè)變量進(jìn)行方法的選擇就叫多分派,Java 靜態(tài)分派(重載)要根據(jù)方法的接收者與參數(shù)這兩個(gè)變量進(jìn)行分配,所以其是多分派。

理解了概念我們接著看我們的案例:

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

我們案例中的accept方法,是由元素的運(yùn)行時(shí)類型決定的,應(yīng)該是屬于動(dòng)態(tài)單分派。

我們接著看 visitor.visit(this)又是一次動(dòng)態(tài)單分派,兩次動(dòng)態(tài)單分派實(shí)現(xiàn)了雙分派的效果,所以稱為偽動(dòng)態(tài)雙分派。

這個(gè)概念理解就好,實(shí)際應(yīng)用中知不知道這玩意都不影響。

五、總結(jié)

當(dāng)你有個(gè)類,里面的包含各種類型的元素,這個(gè)類結(jié)構(gòu)比較穩(wěn)定,不會(huì)經(jīng)常增刪不同類型的元素。而需要經(jīng)常給這些元素添加新的操作的時(shí)候,考慮使用此設(shè)計(jì)模式。

適用對(duì)象結(jié)構(gòu)比較穩(wěn)定每增加一個(gè)元素訪問者都要大變動(dòng),但加新的操作很簡(jiǎn)單。集中相關(guān)的操作、分離無關(guān)的操作。

缺點(diǎn)只有兩個(gè)字-復(fù)雜,號(hào)稱是最復(fù)雜的設(shè)計(jì)模式。

到此這篇關(guān)于Java設(shè)計(jì)模式之訪問者模式的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring Boot 自定義starter的示例代碼

    Spring Boot 自定義starter的示例代碼

    這篇文章主要介紹了Spring Boot 自定義starter的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • Java線程池由淺入深掌握到精通

    Java線程池由淺入深掌握到精通

    什么是線程池?很簡(jiǎn)單,簡(jiǎn)單看名字就知道是裝有線程的池子,我們可以把要執(zhí)行的多線程交給線程池來處理,和連接池的概念一樣,通過維護(hù)一定數(shù)量的線程池來達(dá)到多個(gè)線程的復(fù)用
    2021-09-09
  • java中for循環(huán)刪除集合陷阱

    java中for循環(huán)刪除集合陷阱

    java中在增強(qiáng)for循環(huán)為什么不能增刪集合呢?一個(gè)循環(huán)迭代,跟集合的增刪改沒什么關(guān)系。修改集合不是for去做的。for只管循環(huán)迭代,你在循環(huán)里邊修改集合,改變集合的長(zhǎng)度,順序?qū)ρh(huán)都有影響
    2015-05-05
  • SpringBoot集成Tess4J實(shí)現(xiàn)OCR的示例代碼

    SpringBoot集成Tess4J實(shí)現(xiàn)OCR的示例代碼

    Tess4J是一個(gè)基于Tesseract OCR引擎的Java接口,可以用來識(shí)別圖像中的文本,說白了,就是封裝了它的API,讓Java可以直接調(diào)用,本文給大家介紹了SpringBoot集成Tess4J實(shí)現(xiàn)OCR的示例,需要的朋友可以參考下
    2024-12-12
  • 舉例講解Java設(shè)計(jì)模式中的對(duì)象池模式編程

    舉例講解Java設(shè)計(jì)模式中的對(duì)象池模式編程

    這篇文章主要介紹了Java設(shè)計(jì)模式中的對(duì)象池模式編程示例分享,對(duì)象池模式經(jīng)常在多線程開發(fā)時(shí)被用到,需要的朋友可以參考下
    2016-02-02
  • Java中List集合的常用方法詳解

    Java中List集合的常用方法詳解

    本篇文章給大家?guī)淼膬?nèi)容是關(guān)于Java中List集合的常用方法詳解,有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助。下面我們就來學(xué)習(xí)一下吧
    2021-11-11
  • Idea配置熱部署的詳細(xì)教程

    Idea配置熱部署的詳細(xì)教程

    這篇文章主要介紹了Idea配置熱部署的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java添加Word文本水印和圖片水印

    Java添加Word文本水印和圖片水印

    這篇文章主要介紹了Java添加Word文本水印和圖片水印,文章圖文講解的很清晰,有對(duì)于這方面不懂得同學(xué)可以學(xué)習(xí)下
    2021-02-02
  • SpringBoot中事務(wù)失效的原因詳解

    SpringBoot中事務(wù)失效的原因詳解

    這篇文章主要介紹了SpringBoot中事務(wù)失效的原因詳解,spring中的事務(wù)是依賴AOP的,AOP是通過動(dòng)態(tài)代理實(shí)現(xiàn)的,只有通過代理類訪問的方法才能被攔截,需要的朋友可以參考下
    2023-10-10
  • SpringBoot優(yōu)化接口響應(yīng)時(shí)間的九個(gè)技巧

    SpringBoot優(yōu)化接口響應(yīng)時(shí)間的九個(gè)技巧

    在實(shí)際開發(fā)中,提升接口響應(yīng)速度是一件挺重要的事,特別是在面臨大量用戶請(qǐng)求的時(shí)候,本文為大家整理了9個(gè)SpringBoot優(yōu)化接口響應(yīng)時(shí)間的技巧,希望對(duì)大家有所幫助
    2024-01-01

最新評(píng)論

汉源县| 吉木乃县| 改则县| 上饶县| 连江县| 孟州市| 翁源县| 潼关县| 高台县| 山丹县| 永兴县| 阳曲县| 象山县| 襄垣县| 板桥市| 温宿县| 仁寿县| 福建省| 响水县| 陕西省| 罗山县| 靖江市| 云梦县| 徐汇区| 云霄县| 田阳县| 武威市| 理塘县| 襄垣县| 来宾市| 普格县| 临夏市| 屯门区| 通榆县| 大英县| 石屏县| 海晏县| 喀什市| 上杭县| 江阴市| 政和县|