<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                25.4 訪問者模式的擴展 訪問者模式是經常用到的模式,雖然你不注意,有可能你起的名字也不是什么Visitor,但是它確實是非常容易使用到的,在這里我提出兩個擴展的功能供大家參考。 25.4.1 統計功能 在例子中我們也提到訪問者的統計功能,匯總和報表是金融類企業非常常用的功能,基本上都是一堆的計算公式,然后出一個報表,很多項目采用了數據庫的存儲過程來實現,我不是很推薦這種方式,除非海量數據處理,一個晚上要批處理上億、幾十億條的數據,除了存儲過程來處理還沒有其他辦法,你要是用應用服務器來處理,連接數據庫的網絡就是處于100%占用狀態,一個晚上也未必能處理完這批數據!除了這種海量數據外,我建議數據統計和報表的批處理通過訪問者模式來處理會比較簡單。好,那我們來統計一下公司人員的工資總額,先看類圖,如圖25-6所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036a7fcdf.jpg) 圖25-6 統計功能的訪問者模式 沒什么變化?仔細看IVisitor接口,增加了一個getTotalSalary方法,在Visitor實現類中實現該方法。我們先看接口,如代碼清單25-17所示。 代碼清單25-17 抽象訪問者 public?interface?IVisitor?{ ?????//首先定義我可以訪問普通員工 ?????public?void?visit(CommonEmployee?commonEmployee); ?????//其次定義,我還可以訪問部門經理 ?????public?void?visit(Manager?manager); ?????//統計所有員工工資總和 ?????public?int?getTotalSalary(); } 這就多了一個getTotalSalary方法。我們再來看實現類,如代碼清單25-18所示。 代碼清單25-18 具體訪問者 public?class?Visitor?implements?IVisitor?{ ?????//部門經理的工資系數是5 ?????private?final?static?int?MANAGER_COEFFICIENT?=?5; ?????//員工的工資系數是2 ?????private?final?static?int?COMMONEMPLOYEE_COEFFICIENT?=?2; ?????//普通員工的工資總和 ?????private?int?commonTotalSalary?=?0; ?????//部門經理的工資總和 ?????private?int?managerTotalSalary?=0; ?????//計算部門經理的工資總和 ?????private?void?calManagerSalary(int?salary){ ?????????????this.managerTotalSalary?=?this.managerTotalSalary?+?salary ?????????????*MANAGER_COEFFICIENT?; ?????} ?????//計算普通員工的工資總和 ?????private?void?calCommonSlary(int?salary){ ?????????????this.commonTotalSalary?=?this.commonTotalSalary?+? ?????????????salary*COMMONEMPLOYEE_COEFFICIENT; ?????} ?????//獲得所有員工的工資總和 ?????public?int?getTotalSalary(){ ?????????????return?this.commonTotalSalary?+?this.managerTotalSalary; ?????} } 員工和經理層的信息就不再展示了,請參考代碼清單25-6。程序還是比較簡單的,分別計算普通員工和經理級員工的工資總和,然后加起來。注意,我們在實現時已經考慮員工工資和經理工資的系數不同。 我們再來看Client類的模擬,如代碼清單25-19所示。 代碼清單25-19 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????IVisitor?visitor?=?new?Visitor(); ?????????????for(Employee?emp:mockEmployee()){ ?????????????????????emp.accept(visitor); ?????????????} ?????????????System.out.println("本公司的月工資總額是:"+visitor.getTotalSalary()); ?????} } 其中mockEmployee靜態方法沒有任何改動,請參考代碼清單25-10,在此不再贅述。運行結果如下所示: ? | 姓名:張三 |  性別:男 |  薪水:1800 |  工作:編寫Java程序,絕對的藍領、苦工加搬運工 | |-----|-----|-----|-----| | 姓名:李四 |  性別:女 |  薪水:1900 |  工作:頁面美工,審美素質太不流行了! | | 姓名:王五 |  性別:男 |  薪水:18750 |  業績:基本上是負值,但是我會拍馬屁呀 | 本公司的月工資總額是:101150 然后你想修改工資的系數,沒有問題!想換個展示格式,也沒有問題!多多練習吧,這都是非常簡單的。 25.4.2 多個訪問者 在實際的項目中,一個對象,多個訪問者的情況非常多。其實我們上面例子就應該是兩個訪問者,為什么呢?報表分兩種:第一種是展示表,通過數據庫查詢,把結果展示出來,這個就類似于我們的那個列表;第二種是匯總表,這個是需要通過模型或者公式計算出來的,一般都是批處理結果,這個類似于我們計算工資總額,這兩種報表格式是對同一堆數據的兩種處理方式。從程序上看,一個類就有個不同的訪問者了。修改一下類圖,如圖25-7所示。 類圖看著挺復雜,其實也沒什么復雜的,只是多了兩個接口和兩個實現類,分別負責展示表和匯總表的業務處理,IVisitor接口沒有改變,請參考代碼清單25-5所示代碼,這里不再贅述。我們來看展示報表接口,如代碼清單25-20所示。 代碼清單25-20 展示表接口 public?interface?IShowVisitor?extends?IVisitor?{ ?????//展示報表 ?????public?void?report(); } 展示表的實現也比較簡單,如代碼清單25-21所示。 代碼清單25-21 具體展示表 public?class?ShowVisitor?implements?IShowVisitor?{ ?????private?String?info?=?""; ?????//打印出報表 ?????public?void?report()?{ ??????????System.out.println(this.info); ?????} ?????//訪問普通員工,組裝信息 ?????public?void?visit(CommonEmployee?commonEmployee)?{ ??????????this.info?=?this.info?+?this.getBasicInfo(commonEmployee) ??????????+?"工作:"+commonEmployee.getJob()+"\t\n"; ?????} ?????//訪問經理,然后組裝信息 ?????public?void?visit(Manager?manager)?{ ??????????this.info?=?this.info?+?this.getBasicInfo(manager)?+??"業績: ????????????"+manager.getPerformance()?+?"\t\n"; ?????} ?????//組裝出基本信息 ?????private?String?getBasicInfo(Employee?employee){ ??????????String?info?=?"姓名:"?+?employee.getName()?+?"\t"; ??????????info?=?info?+?"性別:"?+?(employee.getSex()?==?Employee.FEMALE?"女": ??????????"男")?+?"\t"; ??????????info?=?info?+?"薪水:"?+?employee.getSalary()??+?"\t"; ??????????return?info; ?????} } ![](https://box.kancloud.cn/2016-08-14_57b0036a96a7b.jpg) 圖25-7 多訪問者的類圖 匯總表實現數據匯總功能,其接口如代碼清單25-22所示。 代碼清單25-22 匯總表接口 public?interface?ITotalVisitor?extends?IVisitor?{ ?????//統計所有員工工資總和 ?????public?void?totalSalary(); } 就一句話,非常簡單,我們再來看具體的匯總表訪問者,如代碼清單25-23所示。 代碼清單25-23 具體匯總表 public?class?TotalVisitor?implements?ITotalVisitor?{ ?????//部門經理的工資系數是5 ?????private?final?static?int?MANAGER_COEFFICIENT?=?5; ?????//員工的工資系數是2 ?????private?final?static?int?COMMONEMPLOYEE_COEFFICIENT?=?2; ?????//普通員工的工資總和 ?????private?int?commonTotalSalary?=?0; ?????//部門經理的工資總和 ?????private?int?managerTotalSalary?=0; ?????public?void?totalSalary()?{ ??????????System.out.println("本公司的月工資總額是"?+?(this.commonTotalSalary?+ ??????????this.managerTotalSalary)); ?????} ?????//訪問普通員工,計算工資總額 ?????public?void?visit(CommonEmployee?commonEmployee)?{ ??????????this.commonTotalSalary?=?this.commonTotalSalary?+?commonEmployee.getSalary()?*COMMONEMPLOYEE_COEFFICIENT; ?????} ?????//訪問部門經理,計算工資總額 ?????public?void?visit(Manager?manager)?{ ??????????this.managerTotalSalary?=?this.managerTotalSalary?+?manager.getSalary()?*MANAGER_COEFFICIENT?; ?????} } 最后看我們的場景類如何計算出工資總額,如代碼清單25-24所示。 代碼清單25-24 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//展示報表訪問者 ?????????????IShowVisitor?showVisitor?=?new?ShowVisitor(); ?????????????//匯總報表的訪問者 ?????????????ITotalVisitor?totalVisitor?=?new?TotalVisitor(); ?????????????for(Employee?emp:mockEmployee()){ ?????????????????????emp.accept(showVisitor);??//接受展示報表訪問者 ?????????????????????emp.accept(totalVisitor);//接受匯總表訪問者 ?????????????} ?????????????//展示報表 ?????????????showVisitor.report();?????????? ?????????????//匯總報表 ?????????????totalVisitor.totalSalary(); ?????} } 運行結果如下所示: ? | 姓名:張三 |  性別:男 |  薪水:1800 |  工作:編寫Java程序,絕對的藍領、苦工加搬運工 | |-----|-----|-----|-----| | 姓名:李四 |  性別:女 |  薪水:1900 |  工作:頁面美工,審美素質太不流行了! | | 姓名:王五 |  性別:男 |  薪水:18750 |  業績:基本上是負值,但是我會拍馬屁啊 | 本公司的月工資總額是101150 大家可以再深入地想象,一堆數據從幾個角度來分析,那是什么?即數據挖掘(Data Mining),數據的上切、下鉆等處理,大家有興趣看可以翻看數據挖掘或者商業智能(BI)的書。 25.4.3 雙分派 說到訪問者模式就不得不提一下雙分派(double dispatch)問題,什么是雙分派呢?我們先來解釋一下什么是單分派(single dispatch)和多分派(multiple dispatch),單分派語言處理一個操作是根據請求者的名稱和接收到的參數決定的,在Java中有靜態綁定和動態綁定之說,它的實現是依據重載(overload)和覆寫(override)實現的,我們來說一個簡單的例子。 例如,演員演電影角色,一個演員可以扮演多個角色,我們先定義一個影視中的兩個角色:功夫主角和白癡配角,如代碼清單25-25所示。 代碼清單25-25 角色接口及實現類 public?interface?Role?{ ?????//演員要扮演的角色 } public?class?KungFuRole?implements?Role?{ ?????//武功天下第一的角色 } public?class?IdiotRole?implements?Role?{ ?????//一個弱智角色????? } 角色有了,我們再定義一個演員抽象類,如代碼清單25-26所示。 代碼清單25-26 抽象演員 public?abstract?class?AbsActor?{ ?????//演員都能夠演一個角色 ?????public?void?act(Role?role){ ?????????????System.out.println("演員可以扮演任何角色"); ?????}????? ?????//可以演功夫戲 ?????public?void?act(KungFuRole?role){ ?????????????System.out.println("演員都可以演功夫角色"); ?????} } 很簡單,這里使用了Java的重載,我們再來看青年演員和老年演員,采用覆寫的方式來細化抽象類的功能,如代碼清單25-27所示。 代碼清單25-27 青年演員和老年演員 public?class?YoungActor?extends?AbsActor?{ ?????//年輕演員最喜歡演功夫戲 ?????public?void?act(KungFuRole?role){ ?????????????System.out.println("最喜歡演功夫角色"); ?????} } public?class?OldActor?extends?AbsActor?{ ?????//不演功夫角色 ?????public?void?act(KungFuRole?role){ ?????????????System.out.println("年齡大了,不能演功夫角色"); ?????} } 覆寫和重載都已經實現,我們編寫一個場景,如代碼清單25-28所示。 代碼清單25-28 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//定義一個演員 ?????????????AbsActor?actor?=?new?OldActor(); ?????????????//定義一個角色 ?????????????Role?role?=?new?KungFuRole(); ?????????????//開始演戲 ?????????????actor.act(role); ?????????????actor.act(new?KungFuRole()); ?????} } 猜猜看運行結果是什么?很簡單,運行結果如下所示。 演員可以扮演任何角色 年齡大了,不能演功夫角色 重載在編譯器期就決定了要調用哪個方法,它是根據role的表面類型而決定調用act(Role role)方法,這是靜態綁定;而Actor的執行方法act則是由其實際類型決定的,這是動態綁定。 一個演員可以扮演很多角色,我們的系統要適應這種變化,也就是根據演員、角色兩個對象類型,完成不同的操作任務,該如何實現呢?很簡單,我們讓訪問者模式上場就可以解決該問題,只要把角色類稍稍修改即可,如代碼清單25-29所示。 代碼清單25-29 引入訪問者模式 public?interface?Role?{ ?????//演員要扮演的角色 ?????public?void?accept(AbsActor?actor); } public?class?KungFuRole?implements?Role?{ ?????//武功天下第一的角色 ?????public?void?accept(AbsActor?actor){ ?????????????actor.act(this); ?????} } public?class?IdiotRole?implements?Role?{ ?????//一個弱智角色,由誰來扮演 ?????public?void?accept(AbsActor?actor){ ?????????????actor.act(this); ?????} } 場景類稍有改動,如代碼清單25-30所示。 代碼清單25-30 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//定義一個演員 ?????????????AbsActor?actor?=?new?OldActor(); ?????????????//定義一個角色??? ?????????????Role?role?=?new?KungFuRole(); ?????????????//開始演戲 ?????????????role.accept(actor); ?????} } 運行結果如下所示。 年齡大了,不能演功夫角色 看到沒?不管演員類和角色類怎么變化,我們都能夠找到期望的方法運行,這就是雙反派。雙分派意味著得到執行的操作決定于請求的種類和兩個接收者的類型,它是多分派的一個特例。從這里也可以看到Java是一個支持雙分派的單分派語言。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看