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

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

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

    ivaneeo's blog

    自由的力量,自由的生活。

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

    #

    • 作法(Mechanics)
      • 聲明一個常量,令其值為原本的魔法數值.
      • 找出這個魔法數的所有引用點.
      • 檢查是否可以使用這個新聲明的常量來替換該魔法數.如果可以,便以一常量替換之.
      • 編譯.
      • 所有魔法數都被替換完畢后,編譯并測試.此時整個程序應該運轉如常,就像沒有做任何修改一樣.
          • ==>有個不錯的測試辦法:檢查現在的程序是否可以被你輕松地修改常量值(這可能意味某些預期結果將有所改變,以配合這一新值.實際工作中并非總是可以進行這樣的測試).如果可行,這就是一個不錯的手法.
    posted @ 2005-09-15 14:01 ivaneeo 閱讀(365) | 評論 (0)編輯 收藏

    動機(Motivation)
    在計算科學中,魔法數(magic number)是歷史最悠久的不良現象之一.

    進行本項重構之前,你應該先尋找其他替換方案.你應該觀察魔法數如何被使用,而后往往你會發現一種更好的使用方式.如果這個魔法數是個type code(型別碼),請考慮使用Replace Type Code with Class(218);如果這個魔法數代表一個數組的長度,請在遍歷該數組的時候,改用Array.length().
    posted @ 2005-09-15 13:54 ivaneeo 閱讀(391) | 評論 (0)編輯 收藏

    你有一個字面數值(literal number),帶有特殊含義.

    創造一個常量,根據其意義為它命名,并將上述的字面數值替換為這個常量.

    double potentialEnergy(double mass, double height) {
       return mass * 9.81 * height;
    }

                                          126.gif
    double potentialEnergy(double mass, double height) {
       return mass * GRAVITATIONAL_CONSTANT * height;
    }
    static final double GRAVITATIONAL_CONSTANT = 9.81; 
    posted @ 2005-09-15 13:46 ivaneeo 閱讀(403) | 評論 (0)編輯 收藏

    范例(Examples)
    本例從Change Unidirectional association to Bidirectional(197)留下的代碼開始進行,其中CustomerOrder之間有雙向關聯:
    class Order...
       Customer getCustomer() {
          return _customer;
       }
       void setCustomer(Custoemr arg) ...

           if(_customer != null) _customer.friendOrders().remove(this);
           _customer = arg;
           if(_customer != null) _customer.friendOrders().add(this);
        }
       private Customer _customer;   //譯注:這是Order-to-Customer link也是本例的移除對象.

    class Customer ...
        void addOrder(Order arg) {
           arg.setCustomer(this);
        }
       private Set _orders = new HashSet();
       //譯注:以上是Customer-to-Order link
       Set friendOrders() {
           return _orders;
        }


    后來我發現,除非先有Customer對象,否則不會存在Order對象.因此我想將[從Order到Customer的連接]移除掉.

    對于本項重構來說,最困難的就是檢查可行性.如果我知道本項重構是安全的,那么重構手法自身十分簡單.問題在于是否有任何代碼倚賴_customer值域的存在.如果確實有,那么在刪除這個值域之后,我必須提供替代品.

    首先,我需要研究所有讀取這個值域的函數,以及所有使用這些函數的函數.我能找到另一條途徑來供應Customer對象嗎----這通常意味將Customer對象作為引數(argument)傳遞給其用戶(某函數).下面是一個簡化例子:
    class Order...
       double getDiscountedPrice() {
          return getGrossPrice() * (1 - _customer.getDiscount());
       }

    改變為:
    class Order...
       double getDiscountedPrice(Customer customer) {
          return getGrossPrice() * (1 - customer.getDiscount());
       }

    如果待改函數是被Customer對象調用的,那么這樣的修改方案特別容易實施,因為Customer對象將自己作為引數(argument)傳給函數很是容易.所以下列代碼:
    class Customer...
       double getPriceFor(Order order) {
          Assert.isTrue(_orders.contains(order));   //see
    Introduce Assertion(267)
          return order.getDiscountedPrice();

    變成了:
    class Customer...
       double getPriceFor(Order order) {
          Assert.isTrue(_orders.contains(order));
          return order.getDiscountedPrice(this);
    另一個作法就是修改取值函數(getter),使其在不使用_customer值域的前提下返回一個Customer對象.如果這行得通,我就可以使用Substitute Algorithm(139)修改Order.getCustomer()函數算法.我有可能這樣修改代碼:
    Customer getCustomer() {
       Iterator iter = Customer.getInstance().iterator();
       while(iter.hasNext()) {
          Customer each = (Customer)iter.next();
          if(each.containsOrder(this) return each;
       }
       return null;
    }

    這段代碼比較慢,不過確實可行.而且,在數據庫環境下,如果我需要使用數據庫查詢語句,這段代碼對系統性能的影響可能并不顯著.如果,Order class中有些函數使用_customer值域,我可以實施Self Encapsulate Field(171)令它們轉而改用上述的getCustomer()函數.

    如果我要保留上述的取值函數(getter),那么OrderCustomer的關聯從接口上看雖然仍然是雙向,但實現上已經是單向關系了.雖然我移除了反向指針,但兩個classes彼此之間的依存關系(inter-dependencies)仍然存在.

    如果我要替換取值函數(getter),那么我就專注地替換它,其他部分留待以后處理.我會逐一修改取值函數的調用者.讓它們通過其他來源取得Customer對象.每次修改后都編譯并測試.實際工作中這一過程往往相當快.如果這個過程讓我覺得很棘手很復雜,我會放棄本項重構.

    一旦我消除了_customer值域的所有讀取點,我就可以著手處理[對此值域進行賦值動作]的函數了.很簡單,只要把這些賦值動作全部移除,再把值域一并刪除,就行了.由于已經沒有任何代碼需要這個值域,所以刪掉它并不會帶來任何影響.
    posted @ 2005-09-14 17:29 ivaneeo 閱讀(290) | 評論 (0)編輯 收藏

    • 作法(Mechanics)
      • 找出[你想去除的指針]的保存值域,檢查它的每一個用戶,判斷是否可以去除該指針.
          • ==>不但要檢查[直接讀取點],也要檢查[直接讀取點]的調用函數.
          • ==>考慮有無可能不通過指針函數取得[被引用對象](referred object).如果有可能,你就可以對取值函數(getter)使用Substitute Algorithm(139).從而讓客戶在沒有指針的情況下也可以使用該取值函數.
          • ==>對于使用該值域的所有函數,考慮將[被引用對象](referred object)作為引數(argument)傳進去.
      • 如果客戶使用了取值函數(getter),先運用Self Encapsulate Field(171)將[待除值域]自我封裝起來,然后使用Subsitute Algorithm(139)對付取值函數,令它不再使用該(待除)值域.然后編譯,測試.
      • 如果客戶并使用取值函數(getter),那就直接修改[待除值域]的所有被引用點:改以其他途徑獲得該值域所保存的對象.每次修改后,編譯并測試.
      • 如果已經沒有任何函數使用該(待除)值域,移除所有[對該值域的更新邏輯],然后移除該值域.
          • ==>如果有許多地方對此值域賦值,先運用Self Encapsulate Field(171)使這些地點改用同一個設值函數(setter).編譯,測試.而后將這個設值函數的本體清空.再編譯,再測試.如果這些都可行,就可以將此值域和其設值函數,連同對設值函數的所有調用,全部移除.
      • 編譯,測試.
    posted @ 2005-09-14 15:11 ivaneeo 閱讀(255) | 評論 (0)編輯 收藏

    動機(Motivation)
    雙向關聯(bidirectional associations)很有用,但你也必須為它付出代價,那就是[維護雙向鏈接,確保對象被正確創建和刪除]而增加的復雜度.而且,由于很多程序員并不習慣使用雙向關聯,它往往成為錯誤之源.

    大量的雙向連接(two-way links)也很容易引發[僵尸對象]:某個對象本來已經該死亡了,卻仍然保留在系統中,因為對它的各項引用還沒有完全清除.

    此外,雙向關聯也迫使兩個classes之間有了相依性.對其中任一個class的任何修改,都可能引發另一個class的變化.如果這兩個classes處在不同的package中,這種相依性就是packages之間的相依.過多的依存性(inter-dependencies)會造成就緊耦合(highly coupled)系統,使得任何一點小小改動都可能造成許多無法預知的后果.

    只有在你需要雙向關聯的時候,才應該使用它.如果你發現雙向關聯不再有存在價值,就應該去掉其中不必要的一條關聯.
    posted @ 2005-09-14 11:20 ivaneeo 閱讀(301) | 評論 (0)編輯 收藏

    兩個classes之間有雙向關聯,但其中一個class如今不再需要另一個class的特性.

    去除不必要的關聯(association).

    Change Bidirectional  Association to Unidirectional.jpg
    posted @ 2005-09-14 11:08 ivaneeo 閱讀(274) | 評論 (0)編輯 收藏

    范例(Examples)
    下面是一段簡單程序,其中有兩個classes:表示[定單]的Order和表示[客戶]的CustomerOrder引用了CustomerCustomer則并沒有引用Order
    class Order...
        Customer getCustomer() {
           return _customer;
        }
        void setCustomer(Customer arg) {
           _Customer = arg;
        }
        Customer _customer;   //這是一個“Order”to “Customer”的連接

    首先,我要為Customer添加一個值域。由于一個客戶可以擁有多份定單,所以這個新增值域應該是個群集(collection)。我不希望同一份定單在同一個群集中出現一次以上,所以這里適合使用set:
    class Customer {
        private Set _orders = new HashSet();
    現在,我需要決定由哪一個class負責控制關聯性(association)。我比較喜歡讓單一class來操控,因為這樣我就可以將所有[關聯處理邏輯]集中安置于一地。我將按照下列步驟做出這一決定:
    1. 如果兩者都是reference objects,而其間的關聯是[一對多]關系,那么就由[擁有單一reference]的那一方承擔[控制者]角色。以本例而言,如果一個客戶可擁有多份定單,那么就由Order class(定單)來控制關聯性。
    2. 如果某個對象是另一個對象的組成(component),那么由后者負責控制關聯性。
    3. 如果兩者都是reference objects,而其間的關聯是[多對多]關系,那么隨便其中哪個對象來控制關聯性,都無所謂。
    本例之中由于Order負責控制關聯性,所以我必須為Customer添加一個賦值函數,讓Order可以直接訪問_orders(訂單)集群。Order的修改函數(modifier)將使用這個輔助函數對指針兩端對象進行同步控制。我將這個賦值函數命名為friendOrders(),表示這個函數只能在這種特殊情況下使用。此外,如果OrderCustomer位在同一個package內,我還會將friendOrders()聲明為[package  可見度],使其可見程度降至最低。

    但如果這兩個classes不在同一個package內,我就只好把friendOrders()聲明為public了。
    class Customer...
        Set friendOrders() {
           return _orders;
        }
    現在。我要改變修改函數(modifier),令它同時更新反向指針:

    class Order...
        void setCustomer(Custoemr arg) ...
           if(_customer != null) _customer.friendOrders().remove(this);
           _customer = arg;
           if(_customer != null) _customer.friendOrders().add(this);
        }

    classes之間的關聯性是各式各樣的,因此修改函數(modifier)的代碼也會隨之有所差異。如果_customer的值不可能是null,我可 以拿掉上述的第一個null檢查,但仍然需要檢查引數(argument)是否是null。不過,基本形式總是相同的:先讓對方刪除[指向你]的指針,再 將你的指針指向一個新對象,最后讓那個新對象把它的指針指向你。

    如果你希望在Customer中也能修改連接(link),就讓它調用控制函數:

    class Customer ...
        void addOrder(Order arg) {
           arg.setCustomer(this);
        }
    如果一份訂單也可以對應多個客戶,那么你所面臨的就是一個[多對多]情況,重構后的函數可能是下面這樣:
    class Order ...   //controlling methods
        void addCustomer(Customer arg) {
           arg.friendOrders().add(this);
           _customers.add(arg);
        }
        void removeCustomer(Customer arg) {
           arg.friendOrders().remove(this);
           _customers.remove(arg);
        }
    class Customer ...
        void addOrder(Order arg) {
           arg.addCustomer(this);
        }
        void removeOrder(Order arg) {
           arg.removeCustomer(this);
        }
    posted @ 2005-09-13 15:55 ivaneeo 閱讀(323) | 評論 (0)編輯 收藏

    • 作法(Mechanics)
               
        • 在referred class中增加一個值域,用以保存[反向指針].
        • 決定由哪個class(引用端或被引用端)控制關聯性(association).
        • 在[被控制]建立一個輔助函數,其命名應該清楚指出它的有限用途.
        • 如果既有的修改函數(modifier)在[控制端],讓它負責更新反向指針.
        • 如果既有的修改函數(modifier)在[控制端]
    posted @ 2005-09-13 15:00 ivaneeo 閱讀(249) | 評論 (0)編輯 收藏

    開發初期,你可能會在兩個classes之間建立一條單向連接,使其中一個class可以引用另一個class.隨著時間推移,你可能發現referred class需要得到其引用者(某個object)以便進行某些處理.也就是說它需要一個反向指針.

    [反向指針]手法有點棘手,所以在你能夠自在運用它之前,應該有相應的測試.通常我不花心思去測試訪問函數(accessors),因為普通訪問函數的風險沒有高到需要測試的地步,但本重構要求測試訪問函數,所以它是極少數需要添加測試的重構手法之一.

    本重構運用反向指針(back pointer)實現雙向關聯(bidirectionality).其他技術(例如連接對象,link objects)需要其他重構手法.
    posted @ 2005-09-13 10:31 ivaneeo 閱讀(305) | 評論 (0)編輯 收藏

    僅列出標題
    共67頁: First 上一頁 38 39 40 41 42 43 44 45 46 下一頁 Last 
    主站蜘蛛池模板: 国产亚洲视频在线| 丁香婷婷亚洲六月综合色| 国产精品亚洲专区无码不卡| 大地资源二在线观看免费高清 | 亚洲精品成人无限看| 一个人免费观看视频在线中文| 国产免费AV片无码永久免费| 国产亚洲高清在线精品不卡| 五月婷婷亚洲综合| 国产成人无码精品久久久久免费| 国产亚洲精品成人a v小说| 中文字幕看片在线a免费| 亚洲成A人片在线观看WWW| 日韩精品人妻系列无码专区免费| 亚洲第一页在线视频| 成年女人18级毛片毛片免费观看| 亚洲AV无码男人的天堂| 亚洲人成网站在线观看青青| 巨胸狂喷奶水视频www网站免费| 久久久久亚洲AV成人无码| 日韩国产免费一区二区三区| 亚洲精品无码不卡在线播放| 亚洲国产综合久久天堂| 最新久久免费视频| 亚洲午夜电影一区二区三区| 国产成人精品高清免费| 成人性生交大片免费看好| 亚洲一卡2卡4卡5卡6卡在线99| 国产片免费福利片永久| 国产做国产爱免费视频| 亚洲乱码在线视频| 亚洲永久无码3D动漫一区| 日本最新免费网站| 欧洲乱码伦视频免费国产| 久久精品国产亚洲av水果派| 国产大片免费观看中文字幕| 在线成人精品国产区免费| 亚洲熟妇AV日韩熟妇在线| 中文字幕亚洲综合久久菠萝蜜| 亚洲免费黄色网址| 青青青视频免费观看|