<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                33.1 策略模式VS橋梁模式 這對冤家終于碰頭了,策略模式與橋梁模式是如此相似,簡直就是孿生兄弟,要把它們兩個分開可不太容易。我們來看看兩者的通用類圖,如圖33-1所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036ebda54.jpg) 圖33-1 策略模式(左)和橋梁模式(右)通用類圖 兩者之間確實很相似。如果把策略模式的環境角色變更為一個抽象類加一個實現類,或者橋梁模式的抽象角色未實現,只有修正抽象化角色,想想看,這兩個類圖有什么地方不一樣?完全一樣!正是由于類似場景的存在才導致了兩者在實際應用中經常混淆的情況發生,我們來舉例說明兩者有何差別。 大家都知道郵件有兩種格式:文本郵件(Text Mail)和超文本郵件(HTML MaiL),在文本郵件中只能有簡單的文字信息,而在超文本郵件中可以有復雜文字(帶有顏色、字體等屬性)、圖片、視頻等,如果你使用Foxmail郵件客戶端的話就應該有深刻體驗,看到一份郵件,怎么沒內容?原來是你忘記點擊那個“HTML郵件”標簽了。下面我們就來講解如何發送這兩種不同格式的郵件,研究一下這兩種模式如何處理這樣的場景。 33.1.1 策略模式實現郵件發送 使用策略模式發送郵件,我們認為這兩種郵件是兩種不同的封裝格式,給定了發件人、收件人、標題、內容的一封郵件,按照兩種不同的格式分別進行封裝,然后發送之。按照這樣的分析,我們發現郵件的兩種不同封裝格式就是兩種不同的算法,具體到策略模式就是兩種不同策略,這樣看已經很簡單了,我們可以直接套用策略模式來實現。先看類圖,如圖33-2所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036ed20d8.jpg) 圖33-2 策略模式實現郵件發送的類圖 我們定義了一個郵件模板,它有兩個實現類:TextMail(文本郵件)和HtmlMail(超文本郵件),分別實現兩種不同格式的郵件封裝。MailServer是一個環境角色,它接收一個MailTemplate對象,然后通過sendMail方法發送出去。我們來看具體的代碼,先看抽象郵件,如代碼清單33-1所示。 代碼清單33-1 抽象郵件 public?abstract?class?MailTemplate?{ ?????//郵件發件人 ?????private?String?from; ?????//收件人 ?????private?String?to; ?????//郵件標題 ?????private?String?subject; ?????//郵件內容 ?????private?String?context; ?????//通過構造函數傳遞郵件信息 ?????public?MailTemplate(String?_from,String?_to,String?_subject,String?_context){ ?????????????this.from?=?_from; ?????????????this.to?=?_to; ?????????????this.subject?=?_subject; ?????????????this.context?=?_context; ?????} ?????public?String?getFrom()?{ ?????????????return?from; ?????} ?????public?void?setFrom(String?from)?{ ?????????????this.from?=?from; ?????} ?????public?String?getTo()?{ ?????????????return?to; ?????} ?????public?void?setTo(String?to)?{ ?????????????this.to?=?to; ?????} ?????public?String?getSubject()?{ ?????????????return?subject; ?????} ?????public?void?setSubject(String?subject)?{ ?????????????this.subject?=?subject; ?????} ?????public?void?setContext(String?context){ ?????????????this.context?=?context; ?????} ?????//郵件都有內容 ?????public?String?getContext(){ ?????????????return?context; ?????} } 很奇怪,是嗎?抽象類沒有抽象的方法,設置為抽象類還有什么意義呢?有意義,在這里我們定義了一個這樣的抽象類:它具有郵件的所有屬性,但不是一個具體可以被實例化的對象。例如,你對郵件服務器說“給我制造一封郵件”,郵件服務器肯定拒絕,為什么?你要產生什么郵件?什么格式的?郵件對郵件服務器來說是一個抽象表示,是一個可描述但不可形象化的事物。你可以這樣說:“我要一封標題為XX,發件人是XXX的文本格式的郵件”,這就是一個可實例化的對象,因此我們的設計就產生了兩個子類以具體化郵件,而且每種郵件格式對郵件的內容都有不同的處理。我們首先看文本郵件,如代碼清單33-2所示。 代碼清單33-2 文本郵件 public?class?TextMail?extends?MailTemplate?{ ?????public?TextMail(String?_from,?String?_to,?String?_subject,?String?_context)?{ ?????????????super(_from,?_to,?_subject,?_context); ?????} ?????public?String?getContext()?{ ?????????????//文本類型設置郵件的格式為:text/plain ?????????????String?context?=?"\nContent-Type:?text/plain;charset=GB2312\n"?+super.getContext(); ?????????????//同時對郵件進行base64編碼處理,這里用一句話代替 ?????????????context?=?context?+?"\n郵件格式為:文本格式"; ?????????????return?context; ?????} } 我們覆寫了getContext方法,因為要把一封郵件設置為文本郵件必須加上一個特殊的標志:text/plain,用于告訴解析這份郵件的客戶端:“我是一封文本格式的郵件,別解析錯了”。同樣,超文本格式的郵件也有類似的設置,如代碼清單33-3所示。 代碼清單33-3 超文本郵件 public?class?HtmlMail?extends?MailTemplate?{ ?????public?HtmlMail(String?_from,?String?_to,?String?_subject,?String?_context)?{ ?????????????super(_from,?_to,?_subject,?_context); ?????} ?????public?String?getContext(){ ?????????????//超文本類型設置郵件的格式為:multipart/mixed ?????????????String?context?=?"\nContent-Type:?multipart/mixed;?charset=?GB2312\n"?+super.getContext(); ?????????????//同時對郵件進行HTML檢查,是否有類似未關閉的標簽 ?????????????context?=?context?+?"\n郵件格式為:超文本格式"; ?????????????return?context; ?????} } 優秀一點的郵件客戶端會對郵件的格式進行檢查,比如編寫一封超文本格式的郵件,在內容中加上了<font>標簽,但是遺忘了</font>結尾標簽,郵件的產生者(也就是郵件的客戶端)會提示進行修正,我們這里用了“郵件格式為:超文本格式”來代表該邏輯。 兩個實現類實現了不同的算法,給定相同的發件人、收件人、標題和內容可以產生不同的郵件信息。我們看看郵件是如何發送出去的,如代碼清單33-4所示。 代碼清單33-4 郵件服務器 public?class?MailServer?{ ?????//發送的是哪封郵件 ?????private?MailTemplate?m; ?????public?MailServer(MailTemplate?_m){ ?????????????this.m??=?_m; ?????} ?????//發送郵件 ?????public?void?sendMail(){ ?????????????System.out.println("====正在發送的郵件信息===="); ?????????????//發件人 ?????????????System.out.println("發件人:"?+?m.getFrom()); ?????????????//收件人 ?????????????System.out.println("收件人:"?+?m.getTo()); ?????????????//標題 ?????????????System.out.println("郵件標題:"?+?m.getSubject()); ?????????????//郵件內容 ?????????????System.out.println("郵件內容:"?+?m.getContext()); ?????} } 很簡單,郵件服務器接收了一封郵件,然后調用自己的發送程序進行發送。可能讀者要問了,為什么不把sendMail方法移植到郵件模板類中呢?這也是郵件模板類的一個行為,郵件可以被發送。是的,這確實是郵件的一個行為,完全可以這樣做,兩者沒有什么區別,只是從不同的角度看待該方法而已。我們繼續看場景類,如代碼清單33-5所示。 代碼清單33-5 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//創建一封TEXT格式的郵件 ?????????????MailTemplate?m?=?new?HtmlMail("a@a.com","b@b.com","外星人攻擊地球了","結局是外星人被地球人打敗了!"); ?????????????//創建一個Mail發送程序 ?????????????MailServer?mail?=?new?MailServer(m); ?????????????//發送郵件 ?????????????mail.sendMail(); ?????} } 運行結果如下所示: ====正在發送的郵件信息==== 發件人:a@a.com 收件人:b@b.com 郵件標題:外星人攻擊地球了 郵件內容: Content-Type: multipart/mixed;charset=GB2312 結局是外星人被地球人打敗了! 郵件格式為:超文本格式 當然,如果想產生一封文本格式的郵件,只要稍稍修改一下場景類就可以了:new HtmlMail修改為new TextMail,讀者可以自行實現,非常簡單。在該場景中,我們使用策略模式實現兩種算法的自由切換,它提供了這樣的保證:封裝郵件的兩種行為是可選擇的,至于選擇哪個算法是由上層模塊決定的。策略模式要完成的任務就是提供兩種可以替換的算法。 33.1.2 橋梁模式實現郵件發送 橋梁模式關注的是抽象和實現的分離,它是結構型模式,結構型模式研究的是如何建立一個軟件架構,下面我們就來看看橋梁模式是如何構件一套發送郵件的架構的,如圖33-3所示。 類圖中我們增加了SendMail和Postfix兩個郵件服務器來實現類,在郵件模板中允許增加發送者標記,其他與策略模式都相同。我們在這里已經完成了一個獨立的架構,郵件有了,發送郵件的服務器也具備了,是一個完整的郵件發送程序。需要讀者注意的是,SendMail類不是一個動詞行為(發送郵件),它指的是一款開源郵件服務器產品,一般*nix系統的默認郵件服務器就是SendMail;Postfix也是一款開源的郵件服務器產品,其性能、穩定性都在逐步趕超SendMail。 ![](https://box.kancloud.cn/2016-08-14_57b0036eec444.jpg) 圖33-3 橋梁模式實現郵件發送的類圖 我們來看代碼實現,郵件模板僅僅增加了一個add方法,如代碼清單33-6所示。 代碼清單33-6 郵件模板 public?abstract?class?MailTemplate?{ ?????/* ?????*該部分代碼不變,請參考代碼清單33-1 ?????*/????? ?????//允許增加郵件發送標志 ?????public?void?add(String?sendInfo){ ?????????????context?=?sendInfo?+?context; ?????} } 文本郵件、超文本郵件都沒有任何改變,如代碼清單33-2、33-3所示,這里不再贅述。 我們來看郵件服務器,也就是橋梁模式的抽象化角色,如代碼清單33-7所示。 代碼清單33-7 郵件服務器 public?abstract?class?MailServer?{ ?????//發送的是哪封郵件 ?????protected?final?MailTemplate?m; ?????public?MailServer(MailTemplate?_m){ ?????????????this.m??=?_m; ?????} ?????//發送郵件 ?????public?void?sendMail(){ ?????????????System.out.println("====正在發送的郵件信息===="); ?????????????//發件人 ?????????????System.out.println("發件人:"?+?m.getFrom()); ?????????????//收件人 ?????????????System.out.println("收件人:"?+?m.getTo()); ?????????????//標題 ?????????????System.out.println("郵件標題:"?+?m.getSubject()); ?????????????//郵件內容 ?????????????System.out.println("郵件內容:"?+?m.getContext()); ?????} } 該類相對于策略模式的環境角色有兩個改變: ● 修改為抽象類。為什么要修改成抽象類?因為我們在設計一個架構,郵件服務器是一個具體的、可實例化的對象嗎?“給我一臺郵件服務器”能實現嗎?不能,只能說“給我一臺Postfix郵件服務器”,這才能實現,必須有一個明確的可指向對象。 ● 變量m修改為Protected訪問權限,方便子類調用。 我們再來看看Postfix郵件服務器的實現,如代碼清單33-8所示。 代碼清單33-8 Postfix郵件服務器 public?class?Postfix?extends?MailServer?{ ?????public?Postfix(MailTemplate?_m)?{ ?????????????super(_m); ?????} ?????//修正郵件發送程序 ?????public?void?sendMail(){ ?????????????????????//增加郵件服務器信息 ?????????????String?context?="Received:?from?XXXX?(unknown?[xxx.xxx.xxx.xxx])?by?aaa.aaa.com?(Postfix)?with?ESMTP?id?8DBCD172B8\n"?; ?????????????super.m.add(context); ?????????????super.sendMail(); ?????} } 為什么要覆寫sendMail程序呢?這是因為每個郵件服務器在發送郵件時都會在郵件內容上留下自己的標志,一是廣告作用,二是為了互聯網上統計需要,三是方便同質軟件的共振。我們再來看SendMail郵件服務器的實現,如代碼清單33-9所示。 代碼清單33-9 SendMail郵件服務器 public?class?SendMail?extends?MailServer?{ ?????//傳遞一封郵件 ?????public?SendMail(MailTemplate?_m)?{ ?????????????super(_m); ?????} ?????//修正郵件發送程序 ?????@Override ?????public?void?sendMail(){ ?????????????//增加郵件服務器信息 ?????????????super.m.add("Received:?(sendmail);?7?Nov?2009?04:14:44?+0100"); ?????????????super.sendMail(); ?????} } 郵件和郵件服務器都有了,我們來看怎么發送郵件,如代碼清單33-10所示。 代碼清單33-10 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//創建一封TEXT格式的郵件 ?????????????MailTemplate?m?=?new?HtmlMail("a@a.com","b@b.com","外星人攻擊地球了","?結局是外星人被地球人打敗了!"); ?????????????//使用Postfix發送郵件 ?????????????MailServer?mail?=?new?Postfix(m); ?????????????//發送郵件 ?????????????mail.sendMail(); ?????} } 運行結果如下所示: ====正在發送的郵件信息==== 發件人:a@a.com 收件人:b@b.com 郵件標題:外星人攻擊地球了 郵件內容: Content-Type: multipart/mixed;charset=GB2312 Received: from XXXX (unknown [xxx.xxx.xxx.xxx]) by aaa.aaa.com (Postfix) with ESMTP id 8DBCD172B8 結局是外星人被地球人打敗了! 郵件格式為:超文本格式 當然了,還有其他三種發送郵件的方式:Postfix發送文本郵件以及SendMail發送文本郵件和超文本郵件。修改量很小,讀者可以自行修改實現,體會一下橋梁模式的特點。 33.1.3 最佳實踐 策略模式和橋梁模式是如此相似,我們只能從它們的意圖上來分析。策略模式是一個行為模式,旨在封裝一系列的行為,在例子中我們認為把郵件的必要信息(發件人、收件人、標題、內容)封裝成一個對象就是一個行為,封裝的格式(算法)不同,行為也就不同。而橋梁模式則是解決在不破壞封裝的情況下如何抽取出它的抽象部分和實現部分,它的前提是不破壞封裝,讓抽象部分和實現部分都可以獨立地變化,在例子中,我們的郵件服務器和郵件模板是不是都可以獨立地變化?不管是郵件服務器還是郵件模板,只要繼承了抽象類就可以繼續擴展,它的主旨是建立一個不破壞封裝性的可擴展架構。 簡單來說,策略模式是使用繼承和多態建立一套可以自由切換算法的模式,橋梁模式是在不破壞封裝的前提下解決抽象和實現都可以獨立擴展的模式。橋梁模式必然有兩個“橋墩”——抽象化角色和實現化角色,只要橋墩搭建好,橋就有了,而策略模式只有一個抽象角色,可以沒有實現,也可以有很多實現。 還是很難區分,是吧?多想想兩者的意圖,就可以理解為什么要建立兩個相似的模式了。我們在做系統設計時,可以不考慮到底使用的是策略模式還是橋梁模式,只要好用,能夠解決問題就成,“不管黑貓白貓,抓住老鼠的就是好貓”。
                  <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>

                              哎呀哎呀视频在线观看