<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 19, comments - 53, trackbacks - 0, articles - 283
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理


      代碼復(fù)用是絕大多數(shù)程序員所期望的,也是OO的目標(biāo)之一??偨Y(jié)我多年的編碼經(jīng)驗(yàn),為了使代碼能夠最大程度上復(fù)用,應(yīng)該特別注意以下幾個(gè)方面。

      對(duì)接口編程

      "對(duì)接口編程"是面向?qū)ο笤O(shè)計(jì)(OOD)的第一個(gè)基本原則。它的含義是:使用接口和同類型的組件通訊,即,對(duì)于所有完成相同功能的組件,應(yīng)該抽象出一個(gè)接口,它們都實(shí)現(xiàn)該接口。具體到JAVA中,可以是接口(interface),或者是抽象類(abstract class),所有完成相同功能的組件都實(shí)現(xiàn)該接口,或者從該抽象類繼承。我們的客戶代碼只應(yīng)該和該接口通訊,這樣,當(dāng)我們需要用其它組件完成任務(wù)時(shí),只需要替換該接口的實(shí)現(xiàn),而我們代碼的其它部分不需要改變!

      當(dāng)現(xiàn)有的組件不能滿足要求時(shí),我們可以創(chuàng)建新的組件,實(shí)現(xiàn)該接口,或者,直接對(duì)現(xiàn)有的組件進(jìn)行擴(kuò)展,由子類去完成擴(kuò)展的功能。

      優(yōu)先使用對(duì)象組合,而不是類繼承

      "優(yōu)先使用對(duì)象組合,而不是類繼承"是面向?qū)ο笤O(shè)計(jì)的第二個(gè)原則。并不是說繼承不重要,而是因?yàn)槊總€(gè)學(xué)習(xí)OOP的人都知道OO的基本特性之一就是繼承,以至于繼承已經(jīng)被濫用了,而對(duì)象組合技術(shù)往往被忽視了。下面分析繼承和組合的優(yōu)缺點(diǎn):

      類繼承允許你根據(jù)其他類的實(shí)現(xiàn)來定義一個(gè)類的實(shí)現(xiàn)。這種通過生成子類的復(fù)用通常被稱為白箱復(fù)用(white-box reuse)。術(shù)語"白箱"是相對(duì)可視性而言:在繼承方式中,父類的內(nèi)部細(xì)節(jié)對(duì)子類可見。

      對(duì)象組合是類繼承之外的另一種復(fù)用選擇。新的更復(fù)雜的功能可以通過組合對(duì)象來獲得。對(duì)象組合要求對(duì)象具有良好定義的接口。這種復(fù)用風(fēng)格被稱為黑箱復(fù)用(black-box reuse),因?yàn)楸唤M合的對(duì)象的內(nèi)部細(xì)節(jié)是不可見的。對(duì)象只以"黑箱"的形式出現(xiàn)。

      繼承和組合各有優(yōu)缺點(diǎn)。類繼承是在編譯時(shí)刻靜態(tài)定義的,且可直接使用,類繼承可以較方便地改變父類的實(shí)現(xiàn)。但是類繼承也有一些不足之處。首先,因?yàn)槔^承在編譯時(shí)刻就定義了,所以無法在運(yùn)行時(shí)刻改變從父類繼承的實(shí)現(xiàn)。更糟的是,父類通常至少定義了子類的部分行為,父類的任何改變都可能影響子類的行為。如果繼承下來的實(shí)現(xiàn)不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。這種依賴關(guān)系限制了靈活性并最終限制了復(fù)用性。

      對(duì)象組合是通過獲得對(duì)其他對(duì)象的引用而在運(yùn)行時(shí)刻動(dòng)態(tài)定義的。由于組合要求對(duì)象具有良好定義的接口,而且,對(duì)象只能通過接口訪問,所以我們并不破壞封裝性;只要類型一致,運(yùn)行時(shí)刻還可以用一個(gè)對(duì)象來替代另一個(gè)對(duì)象;更進(jìn)一步,因?yàn)閷?duì)象的實(shí)現(xiàn)是基于接口寫的,所以實(shí)現(xiàn)上存在較少的依賴關(guān)系。

      優(yōu)先使用對(duì)象組合有助于你保持每個(gè)類被封裝,并且只集中完成單個(gè)任務(wù)。這樣類和類繼承層次會(huì)保持較小規(guī)模,并且不太可能增長為不可控制的龐然大物(這正是濫用繼承的后果)。另一方面,基于對(duì)象組合的設(shè)計(jì)會(huì)有更多的對(duì)象(但只有較少的類),且系統(tǒng)的行為將依賴于對(duì)象間的關(guān)系而不是被定義在某個(gè)類中。

      注意:理想情況下,我們不用為獲得復(fù)用而去創(chuàng)建新的組件,只需要使用對(duì)象組合技術(shù),通過組裝已有的組件就能獲得需要的功能。但是事實(shí)很少如此,因?yàn)榭捎玫慕M件集合并不豐富。使用繼承的復(fù)用使得創(chuàng)建新的組件要比組裝已有的組件來得容易。這樣,繼承和對(duì)象組合常一起使用。然而,正如前面所說,千萬不要濫用繼承而忽視了對(duì)象組合技術(shù)。

      相關(guān)的設(shè)計(jì)模式有: Bridge、Composite、Decorator、Observer、Strategy等。

      下面的例子演示了這個(gè)規(guī)則,它的前提是:我們對(duì)同一個(gè)數(shù)據(jù)結(jié)構(gòu),需要以任意的格式輸出。

      第一個(gè)例子,我們使用基于繼承的框架,可以看到,它很難維護(hù)和擴(kuò)展。

    abstract class AbstractExampleDocument
    {
        // skip some code ...
        public void output(Example structure)
            {
                if( null != structure )
                    {
                    this.format( structure );
                    }
            }
        protected void format(Example structure);
    }

      第二個(gè)例子,我們使用基于對(duì)象組合技術(shù)的框架,每個(gè)對(duì)象的任務(wù)都清楚的分離開來,我們可以替換、擴(kuò)展格式類,而不用考慮其它的任何事情。

    class DefaultExampleDocument
    {
      // skip some code ...
      public void output(Example structure)
      {
         ExampleFormatter formatter =
           (ExampleFormatter) manager.lookup(Roles.FORMATTER);
         if( null != structure )
         {
           formatter.format(structure);
         }
      }
    }

      這里,用到了類似于"抽象工廠"的組件創(chuàng)建模式,它將組件的創(chuàng)建過程交給manager來完成;ExampleFormatter是所有格式的抽象父類;

      將可變的部分和不可變的部分分離

      "將可變的部分和不可變的部分分離"是面向?qū)ο笤O(shè)計(jì)的第三個(gè)原則。如果使用繼承的復(fù)用技術(shù),我們可以在抽象基類中定義好不可變的部分,而由其子類去具體實(shí)現(xiàn)可變的部分,不可變的部分不需要重復(fù)定義,而且便于維護(hù)。如果使用對(duì)象組合的復(fù)用技術(shù),我們可以定義好不可變的部分,而可變的部分可以由不同的組件實(shí)現(xiàn),根據(jù)需要,在運(yùn)行時(shí)動(dòng)態(tài)配置。這樣,我們就有更多的時(shí)間關(guān)注可變的部分。

      對(duì)于對(duì)象組合技術(shù)而言,每個(gè)組件只完成相對(duì)較小的功能,相互之間耦合比較松散,復(fù)用率較高,通過組合,就能獲得新的功能。

      減少方法的長度

      通常,我們的方法應(yīng)該只有盡量少的幾行,太長的方法會(huì)難以理解,而且,如果方法太長,則應(yīng)該重新設(shè)計(jì)。對(duì)此,可以總結(jié)為以下原則:

      ☆ 三十秒原則:如果另一個(gè)程序員無法在三十秒之內(nèi)了解你的函數(shù)做了什么(What),如何做(How)以及為什么要這樣做(Why),那就說明你的代碼是難以維護(hù)的,必須得到提高;
      ☆ 一屏原則:如果一個(gè)函數(shù)的代碼長度超過一個(gè)屏幕,那么或許這個(gè)函數(shù)太長了,應(yīng)該拆分成更小的子函數(shù);
      ☆ 一行代碼盡量簡短,并且保證一行代碼只做一件事:那種看似技巧性的冗長代碼只會(huì)增加代碼維護(hù)的難度。

      消除case / if語句

      要盡量避免在代碼中出現(xiàn)判斷語句,來測試一個(gè)對(duì)象是否某個(gè)特定類的實(shí)例。通常,如果你需要這么做,那么,重新設(shè)計(jì)可能會(huì)有所幫助。我在工作中遇到這樣的一個(gè)問題:我們?cè)谑褂肑AVA做XML解析時(shí),對(duì)每個(gè)標(biāo)簽映射了一個(gè)JAVA類,采用SAX(簡單的XML接口API:Simple API for XML)模型。結(jié)果,代碼中反復(fù)出現(xiàn)了大量的判斷語句,來測試當(dāng)前的標(biāo)簽類型。為此,我們重新設(shè)計(jì)了DTD(文檔類型定義:Document Type Definition),為每個(gè)標(biāo)簽增加了一個(gè)固定的屬性:classname,而且重新設(shè)計(jì)了每個(gè)標(biāo)簽映射的JAVA類的接口,統(tǒng)一了每個(gè)對(duì)象的操作:
      addElement(Element aElement); //增加子元素
      addAttribute(String attName, String attValue); //增加屬性;

      則徹底消除了所有的測試當(dāng)前的標(biāo)簽類型的判斷語句。每個(gè)對(duì)象通過 Class.forName(aElement.attributes.getAttribute("classname")).newInstence(); 動(dòng)態(tài)創(chuàng)建,

      減少參數(shù)個(gè)數(shù)

      有大量參數(shù)需要傳遞的方法,通常很難閱讀。我們可以將所有參數(shù)封裝到一個(gè)對(duì)象中來完成對(duì)象的傳遞,這也有利于錯(cuò)誤跟蹤。

      許多程序員因?yàn)?,太多層的?duì)象包裝對(duì)系統(tǒng)效率有影響。是的,但是,和它帶來的好處相比,我們寧愿做包裝。畢竟,"封裝"也是OO的基本特性之一,而且,"每個(gè)對(duì)象完成盡量少(而且簡單)的功能",也是OO的一個(gè)基本原則。

      類層次的最高層應(yīng)該是抽象類

      在許多情況下,提供一個(gè)抽象基類有利做特性化擴(kuò)展。由于在抽象基類中,大部分的功能和行為已經(jīng)定義好,使我們更容易理解接口設(shè)計(jì)者的意圖是什么。

      由于JAVA不允許"多繼承",從一個(gè)抽象基類繼承,就無法再從其它基類繼承了。所以,提供一個(gè)抽象接口(interface)是個(gè)好主意,一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,從而模擬實(shí)現(xiàn)了"多繼承",為類的設(shè)計(jì)提供了更大的靈活性。

      盡量減少對(duì)變量的直接訪問

      對(duì)數(shù)據(jù)的封裝原則應(yīng)該規(guī)范化,不要把一個(gè)類的屬性暴露給其它類,而是應(yīng)該通過訪問方法去保護(hù)他們,這有利于避免產(chǎn)生波紋效應(yīng)。如果某個(gè)屬性的名字改變,你只需要修改它的訪問方法,而不是修改所有相關(guān)的代碼。

      子類應(yīng)該特性化,完成特殊功能

      如果一個(gè)子類只是使一個(gè)組件變成組件管理器,而不是實(shí)現(xiàn)接口功能,或者,重載某個(gè)功能,那么,就應(yīng)該使用一個(gè)外部的容器類,而不是創(chuàng)建一個(gè)子類。

      建議:類層次結(jié)構(gòu)圖,不要太深;

      例如:下面的接口定義了組件的功能:發(fā)送消息;類Transceiver實(shí)現(xiàn)了該接口;而其子類Pool只是管理多個(gè)Transceiver對(duì)象,而沒有提供自己的接口實(shí)現(xiàn)。建議使用組合方式,而不是繼承!

    public interface ITransceiver{
      public abstract send(String msg);
    }

    public class Transceiver implements ITransceiver {
      public send(String msg){
        System.out.println(msg);
      }
    }

    //使用繼承方式的實(shí)現(xiàn)
    public class Pool extends Transceiver{
      private List  pool = new Vector();
      public void add(Transceiver aTransceiver){
        pool.add(aTransceiver);
    }
    public Transceiver get(int index){
        pool.get(index);
    }
    }

    //使用組合方式的實(shí)現(xiàn)
    public class Pool {
      private List  pool = new Vector();
      public void add(Transceiver aTransceiver){
        pool.add(aTransceiver);
    }
    public Transceiver get(int index){
        pool.get(index);
    }
    }
     
      拆分過大的類

      如果一個(gè)類有太多的方法(超過50個(gè)),那么它可能要做的工作太多,我們應(yīng)該試著將它的功能拆分到不同的類中,類似于規(guī)則四。

      作用截然不同的對(duì)象應(yīng)該拆分

      在構(gòu)建的過程中,你有時(shí)會(huì)遇到這樣的問題:對(duì)同樣的數(shù)據(jù),有不同的視圖。某些屬性描述的是數(shù)據(jù)結(jié)構(gòu)怎樣生成,而某些屬性描述的是數(shù)據(jù)結(jié)構(gòu)本身。最好將這兩個(gè)視圖拆分到不同的類中,從類名上就可以區(qū)分出不同視圖的作用。

      類的域、方法也應(yīng)該有同樣的考慮!

      盡量減少對(duì)參數(shù)的隱含傳遞

      兩個(gè)方法處理類內(nèi)部同一個(gè)數(shù)據(jù)(域),并不意味著它們就是對(duì)該數(shù)據(jù)(域)做處理。許多時(shí)候,該數(shù)據(jù)(域)應(yīng)該作為方法的參輸入數(shù),而不是直接存取,在工具類的設(shè)計(jì)中尤其應(yīng)該注意。例如:

    public class Test{
      private List  pool = new Vector();
      public void testAdd(String str){
        pool.add(str);
    }
    public Object testGet(int index){
        pool.get(index);
    }
    }

      兩個(gè)方法都對(duì)List對(duì)象pool做了操作,但是,實(shí)際上,我們可能只是想對(duì)List接口的不同實(shí)現(xiàn)Vector、ArrayList等做存取測試。所以,代碼應(yīng)該這樣寫:

    public class Test{
      private List  pool = new Vector();
      public void testAdd(List  pool, String str){
        pool.add(str);
    }
    public Object testGet(List  pool, int index){
        pool.get(index);
    }
    }
     
     
     
     

    主站蜘蛛池模板: 亚洲欧洲日韩国产综合在线二区| 女人张开腿给人桶免费视频| 国产精品免费αv视频| 视频一区在线免费观看| 国产亚洲一卡2卡3卡4卡新区 | www.亚洲色图.com| 国产美女被遭强高潮免费网站| 夫妻免费无码V看片| 免费的一级片网站| 国产一级大片免费看| 亚洲 综合 国产 欧洲 丝袜| 亚洲精品久久久www | 99re这里有免费视频精品| 色欲国产麻豆一精品一AV一免费 | 特黄特色的大片观看免费视频| 日韩精品亚洲专区在线影视| 全黄A免费一级毛片| 一级毛片在线播放免费| 国产福利免费视频| 久久国产精品免费观看| 三年片在线观看免费观看大全一 | 成人性生交大片免费看好| a级毛片毛片免费观看永久| 免费无码av片在线观看| 亚欧免费无码aⅴ在线观看| 国产成人精品免费视频大全麻豆| 免费影院未满十八勿进网站| 香蕉高清免费永久在线视频| 中文字幕无码免费久久99| 在线观看人成网站深夜免费| 国产高清视频在线免费观看| 亚洲黄片手机免费观看| 亚洲精品国产精品乱码在线观看| 久久精品国产亚洲AV无码麻豆| 亚洲不卡1卡2卡三卡2021麻豆| 亚洲精品天堂成人片AV在线播放| rh男男车车的车车免费网站| 久久久久免费精品国产小说| 永久免费av无码不卡在线观看| 亚洲人成人无码网www国产| 亚洲AV无码成人精品区在线观看 |