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

Java設(shè)計(jì)模式之java訪(fǎng)問(wèn)者模式詳解

 更新時(shí)間:2021年09月15日 14:43:59   作者:大忽悠愛(ài)忽悠  
這篇文章主要介紹了JAVA設(shè)計(jì)模式之訪(fǎng)問(wèn)者模式,簡(jiǎn)單說(shuō)明了訪(fǎng)問(wèn)者模式的原理,并結(jié)合實(shí)例分析了java訪(fǎng)問(wèn)者模式的定義與用法,需要的朋友可以參考下

介紹

  • 訪(fǎng)問(wèn)者模式,是行為型設(shè)計(jì)模式之一
  • 訪(fǎng)問(wèn)者模式是一種將數(shù)據(jù)操作與數(shù)據(jù)結(jié)構(gòu)分離的設(shè)計(jì)模式
  • 訪(fǎng)問(wèn)者模式的基本思想: 軟件系統(tǒng)中擁有一個(gè)由許多對(duì)象構(gòu)成的、比較穩(wěn)定的對(duì)象結(jié)構(gòu),這些對(duì)象的類(lèi)都擁有一個(gè) accept 方法用來(lái)接受訪(fǎng)問(wèn)者對(duì)象的訪(fǎng)問(wèn)
  • 訪(fǎng)問(wèn)者是一個(gè)接口,它擁有一個(gè) visit 方法,這個(gè)方法對(duì)訪(fǎng)問(wèn)到的對(duì)象結(jié)構(gòu)中不同類(lèi)型的元素做出不同的處理
  • 在對(duì)象結(jié)構(gòu)的一次訪(fǎng)問(wèn)過(guò)程中,我們遍歷整個(gè)對(duì)象結(jié)構(gòu),對(duì)每一個(gè)元素都實(shí)施 accept 方法,在每一個(gè)元素的 accept 方法中會(huì)調(diào)用訪(fǎng)問(wèn)者的 visit 方法,從而使訪(fǎng)問(wèn)者得以處理對(duì)象結(jié)構(gòu)的每一個(gè)元素,我們可以針對(duì)對(duì)象結(jié)構(gòu)設(shè)計(jì)不同的訪(fǎng)問(wèn)者類(lèi)來(lái)完成不同的操作,達(dá)到區(qū)別對(duì)待的效果。

定義及使用場(chǎng)景

定義:封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作,它可以在不改變這個(gè)數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作。

可以對(duì)定義這么理解:有這么一個(gè)操作,它是作用于一些元素之上的,而這些元素屬于某一個(gè)對(duì)象結(jié)構(gòu)。同時(shí)這個(gè)操作是在不改變各元素類(lèi)的前提下,在這個(gè)前提下定義新操作是訪(fǎng)問(wèn)者模式精髓中的精髓。

使用場(chǎng)景:

(1)對(duì)象結(jié)構(gòu)比較穩(wěn)定,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作。

(2)需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的且不相關(guān)的操作,而需要避免這些操作“污染”這些對(duì)象的類(lèi),也不希望在增加新操作時(shí)修改這些類(lèi)。

UML類(lèi)圖

加粗樣式

角色

(1)Visitor:接口或者抽象類(lèi),它定義了對(duì)每一個(gè)元素(Element)訪(fǎng)問(wèn)的行為,它的參數(shù)就是可以訪(fǎng)問(wèn)的元素,它的方法數(shù)理論上來(lái)講與元素個(gè)數(shù)是一樣的,因此,訪(fǎng)問(wèn)者模式要求元素的類(lèi)族要穩(wěn)定,如果經(jīng)常添加、移除元素類(lèi),必然會(huì)導(dǎo)致頻繁地修改Visitor接口,如果這樣則不適合使用訪(fǎng)問(wèn)者模式。

(2)ConcreteVisitor1、ConcreteVisitor2:具體的訪(fǎng)問(wèn)類(lèi),它需要給出對(duì)每一個(gè)元素類(lèi)訪(fǎng)問(wèn)時(shí)所產(chǎn)生的具體行為。

(3)Element:元素接口或者抽象類(lèi),它定義了一個(gè)接受訪(fǎng)問(wèn)者的方法(Accept),其意義是指每一個(gè)元素都要可以被訪(fǎng)問(wèn)者訪(fǎng)問(wèn)。

(4)ConcreteElementA、ConcreteElementB:具體的元素類(lèi),它提供接受訪(fǎng)問(wèn)方法的具體實(shí)現(xiàn),而這個(gè)具體的實(shí)現(xiàn),通常情況下是使用訪(fǎng)問(wèn)者提供的訪(fǎng)問(wèn)該元素類(lèi)的方法。

(5)ObjectStructure:定義當(dāng)中所說(shuō)的對(duì)象結(jié)構(gòu),對(duì)象結(jié)構(gòu)是一個(gè)抽象表述,它內(nèi)部管理了元素集合,并且可以迭代這些元素供訪(fǎng)問(wèn)者訪(fǎng)問(wèn)。

財(cái)務(wù)案例

  • 財(cái)務(wù)都是有賬本的,這個(gè)賬本就可以作為一個(gè)對(duì)象結(jié)構(gòu),而它其中的元素有兩種,收入和支出,這滿(mǎn)足我們?cè)L問(wèn)者模式的要求,即元素的個(gè)數(shù)是穩(wěn)定的,因?yàn)橘~本中的元素只能是收入和支出。
  • 而查看賬本的人可能有這樣幾種,比如老板,會(huì)計(jì)事務(wù)所的注會(huì),財(cái)務(wù)主管,等等。而這些人在看賬本的時(shí)候顯然目的和行為是不同的。

首先我們給出單子的接口,它只有一個(gè)方法accept。

//單個(gè)單子的接口(相當(dāng)于Element)
public interface Bill
{
    //accept
     void accept(AccountBookViewer accountBookViewer);
}

其中的方法參數(shù)AccountBookViewer是一個(gè)賬本訪(fǎng)問(wèn)者接口,接下來(lái)也就是實(shí)現(xiàn)類(lèi),收入單子和消費(fèi)單子,或者說(shuō)收入和支出類(lèi)。

    //消費(fèi)的單子---抽象賬本的實(shí)現(xiàn)類(lèi)
    public class ConsumeBill implements Bill{
         //消費(fèi)的金額
        private double amount;
        //消費(fèi)的武平
        private String item;
        //構(gòu)造方法為這兩個(gè)屬性賦值
        public ConsumeBill(double amount, String item) {
            super();
            this.amount = amount;
            this.item = item;
        }
        //提供讓訪(fǎng)問(wèn)者訪(fǎng)問(wèn)當(dāng)前賬單中消費(fèi)部分的方法
        public void accept(AccountBookViewer viewer) {
            //調(diào)用訪(fǎng)問(wèn)者的view方法,執(zhí)行對(duì)消費(fèi)部分賬單訪(fǎng)問(wèn)的具體業(yè)務(wù)邏輯
            viewer.view(this);
        }
        public double getAmount() {
            return amount;
        }
        public String getItem() {
            return item;
        }
    }
//收入單子---抽象賬本的另一個(gè)實(shí)現(xiàn)類(lèi)
public class IncomeBill implements Bill{
    private double amount;
    private String item;
    public IncomeBill(double amount, String item) {
        super();
        this.amount = amount;
        this.item = item;
    }
    public void accept(AccountBookViewer viewer) {
        viewer.view(this);
    }
    public double getAmount() {
        return amount;
    }
    public String getItem() {
        return item;
    }
}

上面最關(guān)鍵的還是里面的accept方法,它直接讓訪(fǎng)問(wèn)者訪(fǎng)問(wèn)自己,這相當(dāng)于一次靜態(tài)分派(文章最后進(jìn)行解釋?zhuān)?dāng)然我們也可以不使用重載而直接給方法不同的名稱(chēng)。

接下來(lái)是賬本訪(fǎng)問(wèn)者接口

//賬單查看者接口(相當(dāng)于Visitor)
public interface AccountBookViewer {
    //查看消費(fèi)的單子
    void view(ConsumeBill bill);
    //查看收入的單子
    void view(IncomeBill bill);
}

這兩個(gè)方法是重載方法,就是在上面的元素類(lèi)當(dāng)中用到的,當(dāng)然你也可以按照訪(fǎng)問(wèn)者模式類(lèi)圖當(dāng)中的方式去做,將兩個(gè)方法分別命名為viewConsumeBill和viewIncomeBill,而一般建議按照類(lèi)圖上來(lái)做的

訪(fǎng)問(wèn)者的實(shí)現(xiàn)

//老板類(lèi),查看賬本的類(lèi)之一
public class Boss implements AccountBookViewer{
    private double totalIncome;
    private double totalConsume;
    //老板只關(guān)注一共花了多少錢(qián)以及一共收入多少錢(qián),其余并不關(guān)心
    public void view(ConsumeBill bill) {
        totalConsume += bill.getAmount();
    }
    public void view(IncomeBill bill) {
        totalIncome += bill.getAmount();
    }
    public double getTotalIncome() {
        System.out.println("老板查看一共收入多少,數(shù)目是:" + totalIncome);
        return totalIncome;
    }
    public double getTotalConsume() {
        System.out.println("老板查看一共花費(fèi)多少,數(shù)目是:" + totalConsume);
        return totalConsume;
    }
}
//注冊(cè)會(huì)計(jì)師類(lèi),查看賬本的類(lèi)之一
public class CPA implements AccountBookViewer{
    //注會(huì)在看賬本時(shí),如果是支出,則如果支出是工資,則需要看應(yīng)該交的稅交了沒(méi)
    public void view(ConsumeBill bill) {
        if (bill.getItem().equals("工資")) {
            System.out.println("注會(huì)查看工資是否交個(gè)人所得稅。");
        }
    }
    //如果是收入,則所有的收入都要交稅
    public void view(IncomeBill bill) {
        System.out.println("注會(huì)查看收入交稅了沒(méi)。");
    }
}

老板只關(guān)心收入和支出的總額,而注會(huì)只關(guān)注該交稅的是否交稅

接下來(lái)是賬本類(lèi),它是當(dāng)前訪(fǎng)問(wèn)者模式例子中的對(duì)象結(jié)構(gòu)

//賬本類(lèi)(相當(dāng)于ObjectStruture)
public class AccountBook {
    //單子列表
    private List<Bill> billList = new ArrayList<Bill>();
    //添加單子
    public void addBill(Bill bill){
        billList.add(bill);
    }
    //供賬本的查看者查看賬本
    public void show(AccountBookViewer viewer){
        for (Bill bill : billList) {
            bill.accept(viewer);
        }
    }
}

賬本類(lèi)當(dāng)中有一個(gè)列表,這個(gè)列表是元素(Bill)的集合,這便是對(duì)象結(jié)構(gòu)的通常表示,它一般會(huì)是一堆元素的集合,不過(guò)這個(gè)集合不一定是列表,也可能是樹(shù),鏈表等等任何數(shù)據(jù)結(jié)構(gòu),甚至是若干個(gè)數(shù)據(jù)結(jié)構(gòu)。其中show方法,就是賬本類(lèi)的精髓,它會(huì)枚舉每一個(gè)元素,讓訪(fǎng)問(wèn)者訪(fǎng)問(wèn)。

測(cè)試客戶(hù)端

public class Client {
    public static void main(String[] args) {
        AccountBook accountBook = new AccountBook();
        //添加兩條收入
        accountBook.addBill(new IncomeBill(10000, "賣(mài)商品"));
        accountBook.addBill(new IncomeBill(12000, "賣(mài)廣告位"));
        //添加兩條支出
        accountBook.addBill(new ConsumeBill(1000, "工資"));
        accountBook.addBill(new ConsumeBill(2000, "材料費(fèi)"));
        AccountBookViewer boss = new Boss();
        AccountBookViewer cpa = new CPA();
        //兩個(gè)訪(fǎng)問(wèn)者分別訪(fǎng)問(wèn)賬本
        accountBook.show(cpa);
        accountBook.show(boss);
        ((Boss) boss).getTotalConsume();
        ((Boss) boss).getTotalIncome();
    }
}

在這里插入圖片描述

上面的代碼中,可以這么理解,賬本以及賬本中的元素是非常穩(wěn)定的,這些幾乎不可能改變,而最容易改變的就是訪(fǎng)問(wèn)者這部分。

訪(fǎng)問(wèn)者模式最大的優(yōu)點(diǎn)就是增加訪(fǎng)問(wèn)者非常容易,我們從代碼上來(lái)看,如果要增加一個(gè)訪(fǎng)問(wèn)者,你只需要做一件事即可,那就是寫(xiě)一個(gè)類(lèi),實(shí)現(xiàn)AccountBookViewer接口,然后就可以直接調(diào)用AccountBook的show方法去訪(fǎng)問(wèn)賬本了。

如果沒(méi)使用訪(fǎng)問(wèn)者模式,一定會(huì)增加許多if else,而且每增加一個(gè)訪(fǎng)問(wèn)者,你都需要改你的if else,代碼會(huì)顯得非常臃腫,而且非常難以擴(kuò)展和維護(hù)。

個(gè)人心得體會(huì)

  • 訪(fǎng)問(wèn)者模式將訪(fǎng)問(wèn)者和被訪(fǎng)問(wèn)對(duì)象之間進(jìn)行了解耦,通過(guò)一個(gè)訪(fǎng)問(wèn)者的頂層接口和被訪(fǎng)問(wèn)者的頂層接口達(dá)到這個(gè)目的

靜態(tài)分派以及動(dòng)態(tài)分派

變量被聲明時(shí)的類(lèi)型叫做變量的靜態(tài)類(lèi)型(Static Type),有些人又把靜態(tài)類(lèi)型叫做明顯類(lèi)型(Apparent Type);而變量所引用的對(duì)象的真實(shí)類(lèi)型又叫做變量的實(shí)際類(lèi)型(Actual Type)。比如:

List list = null;
list = new ArrayList();

聲明了一個(gè)變量list,它的靜態(tài)類(lèi)型(也叫明顯類(lèi)型)是List,而它的實(shí)際類(lèi)型是ArrayList。根據(jù)對(duì)象的類(lèi)型而對(duì)方法進(jìn)行的選擇,就是分派(Dispatch),分派(Dispatch)又分為兩種,即靜態(tài)分派和動(dòng)態(tài)分派。靜態(tài)分派(Static Dispatch)發(fā)生在編譯時(shí)期,分派根據(jù)靜態(tài)類(lèi)型信息發(fā)生。

靜態(tài)分派

靜態(tài)分派就是按照變量的靜態(tài)類(lèi)型進(jìn)行分派,從而確定方法的執(zhí)行版本,靜態(tài)分派在編譯時(shí)期就可以確定方法的版本。而靜態(tài)分派最典型的應(yīng)用就是方法重載

public class Main {
    public void test(String string){
        System.out.println("string");
    }
    public void test(Integer integer){
        System.out.println("integer");
    }
    public static void main(String[] args) {
        String string = "1";
        Integer integer = 1;
        Main main = new Main();
        main.test(integer);
        main.test(string);
    }
}

在靜態(tài)分派判斷的時(shí)候,我們根據(jù)多個(gè)判斷依據(jù)(即參數(shù)類(lèi)型和個(gè)數(shù))判斷出了方法的版本,那么這個(gè)就是多分派的概念,因?yàn)槲覀冇幸粋€(gè)以上的考量標(biāo)準(zhǔn),也可以稱(chēng)為宗量。所以JAVA是靜態(tài)多分派的語(yǔ)言。

動(dòng)態(tài)分派

對(duì)于動(dòng)態(tài)分派,與靜態(tài)相反,它不是在編譯期確定的方法版本,而是在運(yùn)行時(shí)才能確定。而動(dòng)態(tài)分派最典型的應(yīng)用就是多態(tài)的特性

interface Person{
    void test();
}
class Man implements Person{
    public void test(){
        System.out.println("男人");
    }
}
class Woman implements Person{
    public void test(){
        System.out.println("女人");
    }
}
public class Main {
    public static void main(String[] args) {
        Person man = new Man();
        Person woman = new Woman();
        man.test();
        woman.test();
    }
}

這段程序輸出結(jié)果為依次打印男人和女人,然而這里的test方法版本,就無(wú)法根據(jù)man和woman的靜態(tài)類(lèi)型去判斷了,他們的靜態(tài)類(lèi)型都是Person接口,根本無(wú)從判斷。

顯然,產(chǎn)生的輸出結(jié)果,就是因?yàn)閠est方法的版本是在運(yùn)行時(shí)判斷的,這就是動(dòng)態(tài)分派。

動(dòng)態(tài)分派判斷的方法是在運(yùn)行時(shí)獲取到man和woman的實(shí)際引用類(lèi)型,再確定方法的版本,而由于此時(shí)判斷的依據(jù)只是實(shí)際引用類(lèi)型,只有一個(gè)判斷依據(jù),所以這就是單分派的概念,這時(shí)我們的考量標(biāo)準(zhǔn)只有一個(gè)宗量,即變量的實(shí)際引用類(lèi)型。相應(yīng)的,這說(shuō)明JAVA是動(dòng)態(tài)單分派的語(yǔ)言。

訪(fǎng)問(wèn)者模式中的偽動(dòng)態(tài)雙分派

訪(fǎng)問(wèn)者模式中使用的是偽動(dòng)態(tài)雙分派,所謂的動(dòng)態(tài)雙分派就是在運(yùn)行時(shí)依據(jù)兩個(gè)實(shí)際類(lèi)型去判斷一個(gè)方法的運(yùn)行行為,而訪(fǎng)問(wèn)者模式實(shí)現(xiàn)的手段是進(jìn)行了兩次動(dòng)態(tài)單分派來(lái)達(dá)到這個(gè)效果。

到上面例子當(dāng)中賬本類(lèi)中的accept方法

for (Bill bill : billList) {
            bill.accept(viewer);
        }

這里就是依據(jù)biil和viewer兩個(gè)實(shí)際類(lèi)型決定了view方法的版本,從而決定了accept方法的動(dòng)作。

分析accept方法的調(diào)用過(guò)程

1.當(dāng)調(diào)用accept方法時(shí),根據(jù)bill的實(shí)際類(lèi)型決定是調(diào)用ConsumeBill還是IncomeBill的accept方法。

2.這時(shí)accept方法的版本已經(jīng)確定,假如是ConsumeBill,它的accept方法是調(diào)用下面這行代碼。

 public void accept(AccountBookViewer viewer) {
        viewer.view(this);
    }

此時(shí)的this是ConsumeBill類(lèi)型,所以對(duì)應(yīng)于AccountBookViewer接口的view(ConsumeBill bill)方法,此時(shí)需要再根據(jù)viewer的實(shí)際類(lèi)型確定view方法的版本,如此一來(lái),就完成了動(dòng)態(tài)雙分派的過(guò)程。

以上的過(guò)程就是通過(guò)兩次動(dòng)態(tài)雙分派,第一次對(duì)accept方法進(jìn)行動(dòng)態(tài)分派,第二次對(duì)view(類(lèi)圖中的visit方法)方法進(jìn)行動(dòng)態(tài)分派,從而達(dá)到了根據(jù)兩個(gè)實(shí)際類(lèi)型確定一個(gè)方法的行為的效果。

而原本我們的做法,通常是傳入一個(gè)接口,直接使用該接口的方法,此為動(dòng)態(tài)單分派,就像策略模式一樣。在這里,show方法傳入的viewer接口并不是直接調(diào)用自己的view方法,而是通過(guò)bill的實(shí)際類(lèi)型先動(dòng)態(tài)分派一次,然后在分派后確定的方法版本里再進(jìn)行自己的動(dòng)態(tài)分派。

注意:這里確定view(ConsumeBill bill)方法是靜態(tài)分派決定的,所以這個(gè)并不在此次動(dòng)態(tài)雙分派的范疇內(nèi),而且靜態(tài)分派是在編譯期就完成的,所以view(ConsumeBill bill)方法的靜態(tài)分派與訪(fǎng)問(wèn)者模式的動(dòng)態(tài)雙分派并沒(méi)有任何關(guān)系。

動(dòng)態(tài)雙分派說(shuō)到底還是動(dòng)態(tài)分派,是在運(yùn)行時(shí)發(fā)生的,它與靜態(tài)分派有著本質(zhì)上的區(qū)別,不可以說(shuō)一次動(dòng)態(tài)分派加一次靜態(tài)分派就是動(dòng)態(tài)雙分派,而且訪(fǎng)問(wèn)者模式的雙分派本身也是另有所指

這里的this的類(lèi)型不是動(dòng)態(tài)確定的,你寫(xiě)在哪個(gè)類(lèi)當(dāng)中,它的靜態(tài)類(lèi)型就是哪個(gè)類(lèi),這是在編譯期就確定的,不確定的是它的實(shí)際類(lèi)型,請(qǐng)各位區(qū)分開(kāi)這一點(diǎn)。

對(duì)訪(fǎng)問(wèn)者模式的一些思考

假設(shè)我們上面的例子當(dāng)中再添加一個(gè)財(cái)務(wù)主管,而財(cái)務(wù)主管不管你是支出還是收入,都要詳細(xì)的查看你的單子的項(xiàng)目以及金額,簡(jiǎn)單點(diǎn)說(shuō)就是財(cái)務(wù)主管類(lèi)的兩個(gè)view方法的代碼是一樣的。

這里的將兩個(gè)view方法抽取的方案是,我們可以將元素提煉出層次結(jié)構(gòu),針對(duì)層次結(jié)構(gòu)提供操作的方法,這樣就實(shí)現(xiàn)了優(yōu)點(diǎn)當(dāng)中最后兩點(diǎn)提到的針對(duì)層次定義操作以及跨越層次定義操作。

//單個(gè)單子的接口(相當(dāng)于Element)
public interface Bill {
    void accept(Viewer viewer);
}
//抽象單子類(lèi),一個(gè)高層次的單子抽象
public abstract class AbstractBill implements Bill{
    protected double amount;
    protected String item;
    public AbstractBill(double amount, String item) {
        super();
        this.amount = amount;
        this.item = item;
    }
    public double getAmount() {
        return amount;
    }
    public String getItem() {
        return item;
    }
}
//收入單子
public class IncomeBill extends AbstractBill{
    public IncomeBill(double amount, String item) {
        super(amount, item);
    }
    public void accept(Viewer viewer) {
        if (viewer instanceof AbstractViewer) {
            ((AbstractViewer)viewer).viewIncomeBill(this);
            return;
        }
        viewer.viewAbstractBill(this);
    }
}
//消費(fèi)的單子
public class ConsumeBill extends AbstractBill{
    public ConsumeBill(double amount, String item) {
        super(amount, item);
    }
    public void accept(Viewer viewer) {
        if (viewer instanceof AbstractViewer) {
            ((AbstractViewer)viewer).viewConsumeBill(this);
            return;
        }
        viewer.viewAbstractBill(this);
    }
}

這是元素類(lèi)的層次結(jié)構(gòu),可以看到,我們的accept當(dāng)中出現(xiàn)了if判斷,這里的判斷是在判斷一個(gè)層次,這段代碼是不會(huì)被更改的。

訪(fǎng)問(wèn)者層次

//超級(jí)訪(fǎng)問(wèn)者接口(它支持定義高層操作)
public interface Viewer{
    void viewAbstractBill(AbstractBill bill);   
}
//比Viewer接口低一個(gè)層次的訪(fǎng)問(wèn)者接口
public abstract class AbstractViewer implements Viewer{
    //查看消費(fèi)的單子
    abstract void viewConsumeBill(ConsumeBill bill);
    //查看收入的單子
    abstract void viewIncomeBill(IncomeBill bill);
    public final void viewAbstractBill(AbstractBill bill){}
}
//老板類(lèi),查看賬本的類(lèi)之一,作用于最低層次結(jié)構(gòu)
public class Boss extends AbstractViewer{
    private double totalIncome;
    private double totalConsume;
    //老板只關(guān)注一共花了多少錢(qián)以及一共收入多少錢(qián),其余并不關(guān)心
    public void viewConsumeBill(ConsumeBill bill) {
        totalConsume += bill.getAmount();
    }
    public void viewIncomeBill(IncomeBill bill) {
        totalIncome += bill.getAmount();
    }
    public double getTotalIncome() {
        System.out.println("老板查看一共收入多少,數(shù)目是:" + totalIncome);
        return totalIncome;
    }
    public double getTotalConsume() {
        System.out.println("老板查看一共花費(fèi)多少,數(shù)目是:" + totalConsume);
        return totalConsume;
    }
}
//注冊(cè)會(huì)計(jì)師類(lèi),查看賬本的類(lèi)之一,作用于最低層次結(jié)構(gòu)
public class CPA extends AbstractViewer{
    //注會(huì)在看賬本時(shí),如果是支出,則如果支出是工資,則需要看應(yīng)該交的稅交了沒(méi)
    public void viewConsumeBill(ConsumeBill bill) {
        if (bill.getItem().equals("工資")) {
            System.out.println("注會(huì)查看是否交個(gè)人所得稅。");
        }
    }
    //如果是收入,則所有的收入都要交稅
    public void viewIncomeBill(IncomeBill bill) {
        System.out.println("注會(huì)查看收入交稅了沒(méi)。");
    }
}
//財(cái)務(wù)主管類(lèi),查看賬本的類(lèi)之一,作用于高層的層次結(jié)構(gòu)
public class CFO implements Viewer {
    //財(cái)務(wù)主管對(duì)每一個(gè)單子都要核對(duì)項(xiàng)目和金額
    public void viewAbstractBill(AbstractBill bill) {
        System.out.println("財(cái)務(wù)主管查看賬本時(shí),每一個(gè)都核對(duì)項(xiàng)目和金額,金額是" + bill.getAmount() + ",項(xiàng)目是" + bill.getItem());
    }
}

財(cái)務(wù)主管(CFO)是針對(duì)AbstractBill這一層定義的操作,而原來(lái)的老板(Boss)和注冊(cè)會(huì)計(jì)師(CPA)都是針對(duì)ConsumeBill和IncomeBill這一層定義的操作,這時(shí)已經(jīng)產(chǎn)生了跨越層次結(jié)構(gòu)的行為,老板和注冊(cè)會(huì)計(jì)師都跨過(guò)了抽象單子這一層,直接針對(duì)具體的單子定義操作。

賬本類(lèi)沒(méi)有變化,最后看客戶(hù)端的使用

public class Client {
    public static void main(String[] args) {
        AccountBook accountBook = new AccountBook();
        //添加兩條收入
        accountBook.addBill(new IncomeBill(10000, "賣(mài)商品"));
        accountBook.addBill(new IncomeBill(12000, "賣(mài)廣告位"));
        //添加兩條支出
        accountBook.addBill(new ConsumeBill(1000, "工資"));
        accountBook.addBill(new ConsumeBill(2000, "材料費(fèi)"));
        Viewer boss = new Boss();
        Viewer cpa = new CPA();
        Viewer cfo = new CFO();
        //兩個(gè)訪(fǎng)問(wèn)者分別訪(fǎng)問(wèn)賬本
        accountBook.show(cpa);
        accountBook.show(boss);
        accountBook.show(cfo);
        ((Boss) boss).getTotalConsume();
        ((Boss) boss).getTotalIncome();
    }
}

回想一下,要是再出現(xiàn)和財(cái)務(wù)主管一樣對(duì)所有單子都是一樣操作的人,我們就不需要復(fù)制代碼了,只需要讓他實(shí)現(xiàn)Viewer接口就可以了,而如果要像老板和會(huì)計(jì)一樣區(qū)分單子的具體類(lèi)型,則繼承AbstractViewer就可以。

在這里插入圖片描述

總結(jié)

優(yōu)點(diǎn)

1、使得數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作解耦,使得操作集合可以獨(dú)立變化。

2、添加新的操作或者說(shuō)訪(fǎng)問(wèn)者會(huì)非常容易。

3、將對(duì)各個(gè)元素的一組操作集中在一個(gè)訪(fǎng)問(wèn)者類(lèi)當(dāng)中。

4、使得類(lèi)層次結(jié)構(gòu)不改變的情況下,可以針對(duì)各個(gè)層次做出不同的操作,而不影響類(lèi)層次結(jié)構(gòu)的完整性。

5、可以跨越類(lèi)層次結(jié)構(gòu),訪(fǎng)問(wèn)不同層次的元素類(lèi),做出相應(yīng)的操作。

缺點(diǎn)

1、增加新的元素會(huì)非常困難。

2、實(shí)現(xiàn)起來(lái)比較復(fù)雜,會(huì)增加系統(tǒng)的復(fù)雜性。

3、破壞封裝,如果將訪(fǎng)問(wèn)行為放在各個(gè)元素中,則可以不暴露元素的內(nèi)部結(jié)構(gòu)和狀態(tài),但使用訪(fǎng)問(wèn)者模式的時(shí)候,為了讓訪(fǎng)問(wèn)者能獲取到所關(guān)心的信息,元素類(lèi)不得不暴露出一些內(nèi)部的狀態(tài)和結(jié)構(gòu),就像收入和支出類(lèi)必須提供訪(fǎng)問(wèn)金額和單子的項(xiàng)目的方法一樣。

適用性

1、數(shù)據(jù)結(jié)構(gòu)穩(wěn)定,這里指的是被訪(fǎng)問(wèn)者的數(shù)據(jù)結(jié)構(gòu)穩(wěn)定,被訪(fǎng)問(wèn)者的類(lèi)內(nèi)部結(jié)構(gòu)和繼承體系不會(huì)有變化,作用于數(shù)據(jù)結(jié)構(gòu)的操作經(jīng)常變化的時(shí)候。

2、當(dāng)一個(gè)數(shù)據(jù)結(jié)構(gòu)中,一些元素類(lèi)需要負(fù)責(zé)與其不相關(guān)的操作的時(shí)候,為了將這些操作分離出去,以減少這些元素類(lèi)的職責(zé)時(shí),可以使用訪(fǎng)問(wèn)者模式。

3、有時(shí)在對(duì)數(shù)據(jù)結(jié)構(gòu)上的元素進(jìn)行操作的時(shí)候,需要區(qū)分具體的類(lèi)型,這時(shí)使用訪(fǎng)問(wèn)者模式可以針對(duì)不同的類(lèi)型,在訪(fǎng)問(wèn)者類(lèi)中定義不同的操作,從而去除掉類(lèi)型判斷。

參考文章

設(shè)計(jì)模式學(xué)習(xí)之訪(fǎng)問(wèn)者模式

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • Spring @Lookup深入分析實(shí)現(xiàn)原理

    Spring @Lookup深入分析實(shí)現(xiàn)原理

    這篇文章主要介紹了Spring @Lookup實(shí)現(xiàn)原理,我們知道在spring容器中單獨(dú)的一個(gè)抽象類(lèi)是不能成為一個(gè)bean的,那么有沒(méi)有辦法呢?這個(gè)時(shí)候我們可以使用Lookup注解
    2023-01-01
  • RocketMQ設(shè)計(jì)之故障規(guī)避機(jī)制

    RocketMQ設(shè)計(jì)之故障規(guī)避機(jī)制

    這篇文章主要介紹了RocketMQ設(shè)計(jì)之故障規(guī)避機(jī)制,故障規(guī)避機(jī)制就是用來(lái)解決當(dāng)Broker出現(xiàn)故障,Producer不能及時(shí)感知而導(dǎo)致消息發(fā)送失敗的問(wèn)題,下面詳細(xì)介紹需要的小伙伴可以參考一下
    2022-03-03
  • Java實(shí)現(xiàn)Excel文件加密解密的示例代碼

    Java實(shí)現(xiàn)Excel文件加密解密的示例代碼

    設(shè)置excel文件保護(hù)時(shí),通??蛇x擇對(duì)整個(gè)工作簿進(jìn)行加密保護(hù)。無(wú)需設(shè)置文檔保護(hù)時(shí),可撤銷(xiāo)密碼保護(hù),即解密文檔。本文將通過(guò)java程序演示以上加密、解密方法的實(shí)現(xiàn),感興趣的可以了解一下
    2022-05-05
  • Java 實(shí)現(xiàn)萬(wàn)年歷總結(jié)

    Java 實(shí)現(xiàn)萬(wàn)年歷總結(jié)

    這篇文章主要介紹了Java 萬(wàn)年歷實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • Java生成隨機(jī)數(shù)的方法

    Java生成隨機(jī)數(shù)的方法

    本篇文章主要介紹了Java生成隨機(jī)數(shù)的方法,具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-05-05
  • springboot啟動(dòng)時(shí)沒(méi)有日志的原因分析

    springboot啟動(dòng)時(shí)沒(méi)有日志的原因分析

    這篇文章主要介紹了springboot啟動(dòng)時(shí)沒(méi)有日志的原因分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring Security異步無(wú)法獲取用戶(hù)認(rèn)證信息的解決方法

    Spring Security異步無(wú)法獲取用戶(hù)認(rèn)證信息的解決方法

    最近使用 Springboot 中 @Async 注解異步調(diào)用方法時(shí),發(fā)現(xiàn)無(wú)法獲取到用戶(hù)認(rèn)證信息,本文小編給大家介紹了Spring Security異步無(wú)法獲取用戶(hù)認(rèn)證信息的原因和解決方法,并通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2024-09-09
  • mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動(dòng)生成的主鍵問(wèn)題

    mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動(dòng)生成的主鍵問(wèn)題

    這篇文章主要介紹了mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動(dòng)生成的主鍵問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 使用@RequestParam 綁定List參數(shù)

    使用@RequestParam 綁定List參數(shù)

    這篇文章主要介紹了使用@RequestParam 綁定List參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java中EasyPoi導(dǎo)出復(fù)雜合并單元格的方法

    Java中EasyPoi導(dǎo)出復(fù)雜合并單元格的方法

    這篇文章主要介紹了Java中EasyPoi導(dǎo)出復(fù)雜合并單元格的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01

最新評(píng)論

浮梁县| 文山县| 红安县| 观塘区| 凤冈县| 青浦区| 阳信县| 张掖市| 通城县| 眉山市| 镇雄县| 呼伦贝尔市| 花莲县| 鄱阳县| 莱西市| 临夏县| 玉田县| 临江市| 吉首市| 南康市| 石首市| 东海县| 荆州市| 分宜县| 城步| 吐鲁番市| 常熟市| 兴安县| 鹤山市| 南丹县| 芜湖县| 佳木斯市| 塘沽区| 丹寨县| 安顺市| 崇义县| 江孜县| 乌海市| 荔波县| 南川市| 枣强县|