總結(jié),記錄  
          日歷
          <2007年7月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234
          統(tǒng)計
          • 隨筆 - 18
          • 文章 - 1
          • 評論 - 5
          • 引用 - 0

          導(dǎo)航

          常用鏈接

          留言簿(1)

          隨筆分類(5)

          隨筆檔案(2)

          收藏夾(1)

          Web Sites

          開一扇門

          搜索

          •  

          積分與排名

          • 積分 - 2797
          • 排名 - 3652

          最新評論

          閱讀排行榜

          評論排行榜

           
              常聽說這么一句話(大意是這樣):不必可以去套用設(shè)計模式,如果按照面向?qū)ο蟮幕驹瓌t編程,自然是優(yōu)雅的設(shè)計,即使沒有刻意使用模式,設(shè)計也會近乎于模 式。開始感覺有一點玄,但在看了《C#設(shè)計模式縱橫談》視頻后,覺得有所收獲。下面,就參考視頻的內(nèi)容,嘗試著寫這么一個過程:根據(jù)面向?qū)ο蟮囊话阍瓌t對設(shè)計進(jìn)行重構(gòu),逐漸演化出觀察者模式。
          涉及的面 向?qū)ο笤O(shè)計原則:單一職責(zé)原則、封裝變化、面向接口編程、依賴倒置原則、開閉原則。

          1.發(fā)布訂閱模型:



                   
                         
          假如有需求如下:

          銀行需要把帳戶的如匯款、轉(zhuǎn)賬或取款等操作通知用戶,途徑包括手機(jī)短信、 email等。如圖所式。

          自然地,我們可以這樣做:

          public class ATM
           {
               BankAccount bankAccount;
               
               
          public void process()
               {
                    
          //bankAccount...
                   this.sendEmail(userEmail);
                   
          this.sendPhone(phoneNumber);
               }
           
               
          private void sendEmail(String userEmail)
               {
                   
          //
               }

              
          private void sendMobile(String phoneNumber)
               {
                   
          //
               }
           }

          ATM機(jī)的 process()方法在處理完業(yè)務(wù)邏輯后,由email和phone通知用戶。

          2.初步重構(gòu)

          好像有bad smells,恩,根據(jù)單一職責(zé)原則。新增Email類和Phone類,并把相關(guān)業(yè)務(wù)邏輯改到BankAccount類完 成。于是我們的代碼可以這樣:


          public class ATM
          {
              BankAccount bankAccount;
              
              
              
          public void process()
              {
                    
          //
                    bankAccount.withDraw();
              }
           
           }

          public class BankAccount 
          {
              Email email;
              Mobile mobile;

              
          public void withDraw()
              {
                   
          //
                   email.sendEmail(userEmail);
                   mobile.sendMobile(phoneNumber);
              }
          }

          public class Email
          {
              
          public void sendEmail(String userEmail)
              {
              }
          }

          public class Mobile
          {
              
          public void sendMobile(String phoneNumber)
              {
              }
          }

          下面是代碼的UML圖:




          3.擁抱變化

          這個解決方案有問題嗎?可能沒有問題。它實 現(xiàn)了我們的需求:在帳戶有操作變動的時候,通知Email和Mobile去發(fā)送信息給用戶。但這樣設(shè)計就足夠了嗎?可能足夠了,可能還不夠。
          考慮如下兩種情況:
          1.在很長一段時間里,訂閱方式很穩(wěn)定,比如系統(tǒng)只通過郵件和手機(jī)短信進(jìn)行信息訂閱,那么這個實現(xiàn)沒 有太大問題;
          2.在近一兩年或更短的時間,更多的訂閱方式將會源源不斷地被加進(jìn)來:比如可以登錄官方網(wǎng)站等等,那這個實現(xiàn)就有問題: 再看一下我們的UML圖,類BankAccount依賴于Email和Mobile類!就是說,如果需要添加新的訂閱方式ATM類的process()方 法勢必要重新設(shè)計!

          于是我們的BankAccount類不得不變成:

          public class  BankAccount
          {
              Email email;
              Mobile mobile;
              Web web;

              
          public void withDraw()
              {
                    
          //
                   email.sendEmail(userEmail);
                   mobile.sendMobile(phoneNumber);
                   web.sendWeb(webSite);
              }
           
           }

          如果還有另一種方式,那么process()方法就又會需要加入:otherSubscribe.send...();等方法,另外 如果訂閱類的接口(這里指sendEmail等方法)發(fā)生變化,BankAccount的withDraw()方法也必須有相應(yīng)的變化!這當(dāng)然是種災(zāi)難。 我們必須改變這種情況。
          先解決遺留問題:第一種情況:訂閱方式相對穩(wěn)定的情況下呢?不改動會產(chǎn)生災(zāi)難嗎?
          個人認(rèn) 為:不會。比如某個系統(tǒng)信息只通過手機(jī)短信訂閱,那就沒有必要太在意這個問題??紤]周全一點不好嗎,如果將來有類似需求呢?小心過度設(shè)計!為了將來可能出現(xiàn)需求而進(jìn)行的預(yù)先設(shè)計并不太好。有需求,才有設(shè)計。

          現(xiàn)在來看解決之道:

          運(yùn)用面向?qū)ο蟮乃枷耄橄蟪鰡栴}所在。BankAccount類依賴于 Email類和Mobile類,而Email和Mobile是具體的類,ATM依賴于具體的類了,而且還不止一個!回憶一下依賴倒置原則:具體應(yīng)該依賴于抽象,底層模式應(yīng)該依賴于高層模式。那怎么實現(xiàn)依賴倒置原則呢?面向?qū)ο缶幊讨杏幸粭l總的原則:封裝變化。如何實現(xiàn)封裝變化?需要我們這樣:面向接口編程。

          回顧一下:我們在設(shè)計中實現(xiàn)類依賴了具體的類,違反了依賴倒置原則。為了遵循依賴倒置原則,我們采用面向接口編程的方法,從而 實現(xiàn)了面向?qū)ο蟮囊粭l總的原則:封裝變化。


          看代碼:

          public interface AccountObserver
          {
              
          public void upDate(UserAccount userAccount);
          }

          public class Email implements AccountObserver
          {
              
          public void upDate(UserAccount userAccount)
              {
              }
          }

          public class Mobile
          {
              
          public void upDate(UserAccount userAccount)
              {
              }
          }

          public class BankAccount 
          {
              List 
          <AccountObserver> observer = new ArrayList<AccountObserver>;

              
          public void withDraw()
              {
                   
          //
                   for (AccountObserver ao : observer)
                   {
                      ao.upDate(userAccount)
                    }
              }
              
              
          public void addOberver(AccountObserver accountObserver)
              {
                    observer.add(accountObserver);
               }
          }

          UML圖:



          現(xiàn)在,BankAccount依賴于interface AccountObserver。Email和Mobile實現(xiàn)AccountObserver接口。通過遵循面向接口編程遵循了依賴倒置原則

          4.開閉原則

          終于修改好了,我們解決了訂閱者變化的問 題。但如果發(fā)布者也傾向于變化呢?這就牽涉到面向?qū)ο罄锏牧硪粋€原則:開閉原則。即:對擴(kuò)展開放,對修改關(guān)閉。具體怎么做呢?通過抽象類,從抽象類繼承具體類。
          看最終的代碼(只寫幾個關(guān)鍵的方法,全貌可看最后的UML圖):

          訂閱:

          public interface AccountObserver
          {
              
          public void upDate(UserAccount userAccount);
          }

          public class Email implements AccountObserver
          {
              
          public void upDate(UserAccount userAccount)
              {
              }
          }

          public class Mobile implements AccountObserver
          {
              
          public void upDate(UserAccount userAccount)
              {
              }
          }


          發(fā)布:



          public abstract class Subject
          {
               List 
          <AccountObserver> observer = new ArrayList<AccountObserver>;

              
          protected void withDraw()
              {
                   
          //
                   notify();
                   
              }
              
              
          protected void notify(UserAccount userAccount)
              {
                   
          for (AccountObserver ao : observer)
                   {
                      ao.upDate(userAccount)
                    }
               }
              
              
          protected void addOberver(AccountObserver accountObserver)
              {
                    observer.add(accountObserver);
               }

                
          protected void deleteOberver(AccountObserver accountObserver)
              {
                    observer.remove(accountObserver);
               }

          }

          public class BankAccount extends Subject
          {
              
          public void withDraw()
              {
                   
          //
                   for (AccountObserver ao : observer)
                   {
                      ao.upDate(userAccount)
                    }
              }
              
          }



          看UML圖:




          5.觀察者模式概況




          這就是觀察者模式了,對比一下官方的UML圖,是不是一目了然了呢?
          稍作說明(這里的依賴 都是指廣義的依賴):
          1.被觀察者ConcreteSubject繼承自Subject抽象類;
          2.Subject抽象類依賴于觀察者Observer抽象接口;
          3.觀察者ConcreteObserver實現(xiàn)Observer 接口;
          4.觀察者ConcreteObserver間接依賴于ConcreteSubject類。
          如果要增加具體的觀察者,只要再實現(xiàn)Obsever接口即可,而被觀察方不需要做任何修改。而如果需要修改被觀察者,只要從Subject抽 象類繼承即可。
          posted on 2007-07-10 00:11 L.X 閱讀(1355) 評論(3)  編輯  收藏 所屬分類: Design Patterns
          評論:
          • # re: Refactoring To Patterns: 觀察者模式  Sun Posted @ 2007-07-10 08:40
            我這個好像比較容易看懂
            http://www.aygfsteel.com/Swing/archive/2007/07/09/128970.html  回復(fù)  更多評論   

          • # re: Refactoring To Patterns: 觀察者模式  stoneshao Posted @ 2007-07-10 10:04
            不錯,寫得清晰易懂  回復(fù)  更多評論   

          • # re: Refactoring To Patterns: 觀察者模式  10:10 Posted @ 2007-07-10 10:28
            thx樓上兩位,^_^

            一樓哥們里的blog文章是
            板橋里人 http://www.jdon.com 寫的:
            設(shè)計模式之Observer
            http://www.jdon.com/designpatterns/observer.htm

            但我覺得反而從重構(gòu)的角度理解Observer要好一些:設(shè)計模式是工具,用來理解面向?qū)ο笏枷?,比直接拿來用更有意義。我深以為然。

            當(dāng)然,我寫出學(xué)習(xí)筆記,很高興能得到回應(yīng)和討論。  回復(fù)  更多評論   


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
           
          Copyright © L.X Powered by: 博客園 模板提供:滬江博客
          主站蜘蛛池模板: 安丘市| 沙洋县| 碌曲县| 德格县| 蒲城县| 平度市| 武城县| 华坪县| 无极县| 新密市| 昭平县| 兴仁县| 张家口市| 安义县| 集安市| 依安县| 宜章县| 南澳县| 南康市| 文水县| 育儿| 皋兰县| 昭平县| 凌海市| 彩票| 江油市| 开化县| 宜君县| 九江市| 馆陶县| 宿州市| 靖远县| 绵阳市| 满城县| 临沭县| 阳信县| 阿坝县| 芒康县| 沙雅县| 大厂| 明水县|