John Jiang

          a cup of Java, cheers!
          https://github.com/johnshajiang/blog

             :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
            131 隨筆 :: 1 文章 :: 530 評論 :: 0 Trackbacks
          編寫好的面向?qū)ο蟠a

              本文是java.net上的一篇博客,作者Curtis Cooley對編寫好的面向?qū)ο蟠a有些建議,希望對大家都有所幫助。(2008.10.08最后更新)

          獲取經(jīng)驗(yàn)沒有捷徑。編寫好的面向?qū)ο蟠a需要經(jīng)驗(yàn),但這兒有三種做法能幫你在一開始就很順利,即便你是老頑固:
              1. 使用測試驅(qū)動開發(fā)(TDD)編寫你所有的代碼
              2. 遵循簡單法則
              3. 告之而非問之

          使用TDD編寫所有代碼
              按測試先行編寫的代碼與按測試后行編寫的代碼是極為不同的代碼。按測試先行編寫的代碼是松耦合與高聚合的。當(dāng)某個(gè)屬性或私有方法需要暴露給測試程序時(shí),按測試后行編寫的代碼常會打破封裝,因?yàn)樵擃惒⒉皇菫榱藴y試而設(shè)計(jì)的。如果你首先編寫測試代碼,你的依賴將會更好,你的代碼將是松耦合與高聚合的。后面會有更多關(guān)于測試能幫助你設(shè)計(jì)更佳代碼的內(nèi)容。

          遵循簡單法則
              代碼是簡單的,只要當(dāng)它:
              1. 執(zhí)行了所有的測試
              2. 不包含重復(fù)
              3. 表達(dá)了所有的意圖
              4. 使用最少的類和方法
          注意到我用的是個(gè)被排序了的列表是很重要的。順序是重要的。只有一個(gè)main()方法的的GodClass[1]不會是簡單的。這個(gè)類可能執(zhí)行了所有的測試,但在任何比"Hello, world!"更復(fù)雜的程序中,它肯定包含了重復(fù),并且也沒有表達(dá)出全部的意圖。
          我努力使用簡單法則去關(guān)注If問題。我不知道如何使用簡單法則去阻止某人編寫重量級的If代碼。有人可能會提出不同意見,我也嘗試過,但這樣的重量級If代碼確實(shí)無法表達(dá)意圖。但當(dāng)你閱讀如下代碼時(shí)

          if (mobile.getType() == MobileTypes.STANDARD) {
            alert();
          }
          確實(shí)難以看出其中的意圖。這些代碼無論處于哪個(gè)方法的上下文環(huán)境中,我們都能知道,如果mobile是STANDARD類型的話,那么就報(bào)警。而你所需要的更多意圖呢?
          我還有一點(diǎn)兒靈感顯現(xiàn)。如果有那樣的代碼,那么在其它地方肯定還會有更多那樣的代碼。這些代碼可能就像:
          if (mobile.getType() == MobileTypes.GAS) {
            registerGasReading();
          }

          if (mobile.getType() == MobileTypes.TEXT) {
            sendTextMessage();
          }

          if (mobile.getType() == MobileTypes.LOCATION) {
            notifyLocation();
          }
          你看出來了嗎?我是看出來了。它違反了規(guī)則2,有很多地方都違反了規(guī)則2,并且是一種最壞的情形。這段代碼有多處重復(fù)。重復(fù)將極難發(fā)現(xiàn)。所以,請幫助防止這種情形的發(fā)生,我已包含其中了。

          告之而非問之
          簡言之,告之而非問之意指不要先問一個(gè)對象的狀態(tài),然后才讓它去工作。而應(yīng)該告之對象如何去工作。這就意味著之前所有的那些If例子應(yīng)該變?yōu)椋?/span>
          mobile.alert();

          mobile.registerGasReading();

          mobile.sendTextMessage();

          mobile.notifyLocation();
          現(xiàn)假設(shè)遍布該程序中的一些If語句塊有重復(fù)的實(shí)現(xiàn)。在"重量級If"版本的程序中,可能很難發(fā)現(xiàn)它們;但在"告之而非問之"版本的程序中,所有的實(shí)現(xiàn)都在Mobile中。所有的實(shí)現(xiàn)都在一處,這就便于察覺并根除問題。
              傾聽你的測試程序也能幫助你保持代碼的簡潔。

          public interface Alarm {
            
          void alert(Mobile mobile);
          }

          public class Siren implements Alarm {
            
          public void alert(Mobile mobile) {
              
          if (mobile.getType == MobileTypes.STANDARD) {
                soundSiren();
              }
            }
          }

          public class TestSiren extends TestCase {
            
          public void test_alert() {
              LocationMobile mobile 
          = new LocationMobile();
              Siren siren 
          = new Siren();
              siren.alert(mobile);
              
          assert(sirenSounded());
            }
          }
          如果你密切地傾聽測試程序,它可能會問你,"為什么你需要一個(gè)LocationMobile去測試Siren呢?"的確,為什么呢?看起來,Siren應(yīng)該還不知道LocationMobile吧。
          public class LocationMobile {
            
          private Alarm alarm;
            
          public LocationMobile(Alarm alarm) {
              
          this.alarm = alarm;
            }
            
          public void alert() {
              alarm.alert(); 
          // alert on Alarm no longer needs a mobile
            }
          }

          public class TestLocationMobile() extends TestCase {
            
          public void test_alert() {
              Alarm alarm 
          = EasyMock.createMock(Alarm.class);
              alarm.alert();
              EasyMock.replay(alarm);
              Mobile mobile 
          = new LocationMobile(alarm);
              mobile.alert();
              EasyMock.verify(alarm);
          }
          好像我只是交換了依賴關(guān)系。Alarm不再依賴Mobile,現(xiàn)在是Mobile依賴Alarm。但如果你仔細(xì)地觀察這個(gè)測試程序,你會發(fā)現(xiàn)真正的依賴關(guān)系是,Siren知曉了LocationMobile。一個(gè)具體類依賴另一個(gè)具體類,這違反了依賴反轉(zhuǎn)原則(DIP)。第二個(gè)例子就讓LocationMobile依賴Alarm接口。具體類依賴抽象,這就滿足DIP了。
              如果你使用TDD,并遵循簡單法則和告之而非問之原則去編寫所有的代碼,你就處于成為一個(gè)更好的面向?qū)ο蟪绦騿T的道路上了。好的面向?qū)ο蟠a易于閱讀和維護(hù),但難以編寫,至少,在開始時(shí)是這樣的。你寫的越多,你就會變得越好,也會獲得更多的經(jīng)驗(yàn)。同時(shí),這些實(shí)踐經(jīng)驗(yàn)也會使你在自己的道路上受益匪淺。

          譯注
          [1]GodClass(上帝類)指包含了太多內(nèi)容的類。
          posted on 2008-10-07 17:06 John Jiang 閱讀(1881) 評論(7)  編輯  收藏 所屬分類: Java翻譯UnitTestMethodology

          評論

          # re: 編寫好的面向?qū)ο蟠a(譯) 2008-10-08 08:57 Jack.Wang
          very good, well done boy!  回復(fù)  更多評論
            

          # re: 編寫好的面向?qū)ο蟠a(譯) 2008-10-08 09:05 Sha Jiang
          Thanks :-  回復(fù)  更多評論
            

          # re: 編寫好的面向?qū)ο蟠a(譯) 2008-10-08 11:15 大衛(wèi)
          Thank you very much!  回復(fù)  更多評論
            

          # re: 編寫好的面向?qū)ο蟠a(譯) 2008-10-08 12:42 Sha Jiang
          @大衛(wèi)
          我對TDD也沒什么研究,興趣倒是有一些。
          但在實(shí)際的工作中,我沒有應(yīng)用過TDD,只是使用過單元測試罷了。  回復(fù)  更多評論
            

          # re: 編寫好的面向?qū)ο蟠a(譯) 2008-10-10 15:37 戰(zhàn)爭與和平
          實(shí)話實(shí)說,沒太明白。
          1. 告之而非問之:不用if怎么進(jìn)行流程控制?
          2. 交換依賴關(guān)系的描述明白,不過例子不甚明白。  回復(fù)  更多評論
            

          # re: 編寫好的面向?qū)ο蟠a(譯) 2008-10-11 10:09 大衛(wèi)
          @戰(zhàn)爭與和平
          其實(shí)就是一個(gè)再封裝的問題,將復(fù)雜的東西封裝起來。簡化上層邏輯并使之清晰。  回復(fù)  更多評論
            

          # re: 編寫好的面向?qū)ο蟠a(譯) 2008-10-19 15:02 謝亞力·帕它
          活著正好
            回復(fù)  更多評論
            

          主站蜘蛛池模板: 福建省| 福海县| 墨江| 枞阳县| 寿阳县| 开封市| 呼伦贝尔市| 连城县| 德阳市| 高碑店市| 东乡族自治县| 汤阴县| 茶陵县| 新平| 类乌齐县| 高州市| 青岛市| 天峻县| 鄂托克前旗| 保亭| 灌云县| 湾仔区| 航空| 尼勒克县| 灵台县| 正定县| 扎赉特旗| 麻阳| 岑溪市| 依安县| 长宁县| 江津市| 广饶县| 微博| 依兰县| 图木舒克市| 友谊县| 靖宇县| 绥宁县| 鹤岗市| 博野县|