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

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ### Introduce Null Object(引入Null 對象) 你需要再三檢查「某物是否為null value」。 將null value (無效值)替換為null object(無效物)。 ~~~ if (customer == null) plan = BillingPlan.basic(); else plan = customer.getPlan(); ~~~ => ![](https://box.kancloud.cn/2016-08-15_57b1b5aa3516f.gif) **動機(Motivation)** 多態(polymorphism )的最根本好處在于:你不必再向對象詢問「你是什么型別」 而后根據得到的答案調用對象的某個行為——你只管調用該行為就是了,其他的一切多態機制會為你安排妥當。當你的某個值域內容是null value 時,多態可扮演另一個較不直觀(亦較不為人所知)的用途。讓我們先聽聽Ron Jeffries 的故事。 Ron Jeffries 我們第一次使用Null Object 模式,是因為Rih Garzaniti 發現,系統在對對象發送一個消息之前,總要檢査對象是否存在,這樣的檢査出現很多次。我們可能會向一個對象索求它所相關的Person 對象,然后再問那個對象是否為null 。如果對象的確存在,我們才能調用它的rate() 函數以查詢這個人的薪資級別。我們在好些地方都是這樣做的, 造成的重復代碼讓我們很煩心。 所以.我們編寫了一個MissingPerson class,讓它返回 '0' 薪資等級(我們把null objects 稱為missing object(虛構對象)。很快地MissingPerson 就有了很多函數,rate() 自然是其中之一。如今我們的系統有超過80個null object classes。 我們常常在顯示信息的時候使用null object。例如我們想要顯示一個Person 對象信息,它大約有20個instance 變量。如果這些變量可被設為null,那么打印一個Person 對象的工作將非常復雜。所以我們不讓instance 變量被設為null ,而是插入各式各樣的null objects ——它們都知道如何正常(正確地)顯示自己。這樣,我們就可以擺脫大量代碼。 我們對null object 的最聰明運用,就是拿它來表示不存在的Gemstone session。我們使用Gemstone 數據庫來保存成品(程序代碼),但我們更愿息在沒有數據庫的情況下進行開發,毎過一周左右再把新碼放進Gemstone 數據庫。然而在代碼的某些地方,我們必須登錄(log in)一個Gemstone session。當我們沒有Gemstone 數據庫時,我們就僅僅安插一個miss Gemstone session,其接口和真正的Gemstone session 一模一樣,使我們無需判斷數據庫是否存在,就可以進行開發和測試。 null object 的另一個用途是表現出「虛構的箱倉」(missing bin)。所謂「箱倉],這里是指群集(collection),用來保存某些薪資值,并常常謠要對各個薪資值進行加和或遍歷。如果某個箱倉不存在,我們就給出一個虛構的箱倉對象,其行為和一個空箱倉(empty bin)一樣;這個虛構箱倉知道自己其實不帶任何數據,總值為0。通過這種作法,我們就不必為上千位員工每人產生數十來個空箱(empty bins)對象了。 使用null objects 有個非常有趣的性質:好事絕對不會因為null objects 而「被破壞」。由于null objects 對所有外界請求的響應,都像real objects 的響應一樣,所以系統行為總是正常的。但這并非總是好事,有吋會造成問題的偵測和查找上的困難,因為從來沒有任何東西被破壞。當然,只要認真檢查一下,你就會發現null objects 有時出現在不該出現的地方。 請記住:null objects 一定是常量,它們的任何成分都不會發生變化。因此我們可以使用Singleton 模式[Gang of Four]來實現它們。例如不管任何時候,只要你索求一個MissingPerson 對象,你得到的一定是MissingPerson 的惟一實體。 關于Null Object 模式,你可以在Woolf [Woolf] 中找到更詳細的介紹。 **作法(Mechanics)** - 為source class 建立一個subclass ,使其行為像source class 的null 版本。在source class 和null class 中都加上isNull() 函數,前者的isNull() 應該返回false,后者的isNull() 應該返回true。 - 下面這個辦法也可能對你有所幫助:建立一個nullable 接口,將isNull() 函數放在其中,讓source class 實現這個接口。 - 另外,你也可以創建一個testing 接口,專門用來檢查對象是否為null。 - 編譯。 - 找出所有「索求source object 卻獲得一個null 」的地方。修改這些地方,使它們改而獲得一個null object。 - 找出所有「將source object 與null 做比較」的地方。修改這些地方,使它們調用isNull() 函數。 - 你可以每次只處理一個source object 及其客戶程序,編譯并測試后, 再處理另一個source object 。 - 你可以在「不該再出現null value」的地方放上一些assertions(斷言), 確保null 的確不再出現。這可能對你有所幫助。 - 編譯,測試。 - 找出這樣的程序點:如果對象不是null ,做A動作,否則做B 動作。 - 對于每一個上述地點,在null class 中覆寫A動作,使其行為和B 動作相同。 - 使用上述的被覆寫動作(A),然后刪除「對象是否等于null」的條件測試。編譯并測試。 **范例:(Example)** —家公用事業公司的系統以Site 表示地點(場所)。庭院宅等和集合公寓(apartment)都使用該公司的服務。任何時候每個地點都擁有(或說都對應于)一個顧客,顧客信息以Customer 表示: ~~~ class Site... Customer getCustomer() { return _customer; } Customer _customer; ~~~ Customer 有很多特性,我們只看其中三項: ~~~ class Customer... public String getName() {...} public BillingPlan getPlan() {...} public PaymentHistory getHistory() {...} ~~~ 本系統又以PaymentHistory 表示顧客的付款記錄,它也有它自己的特性: ~~~ public class PaymentHistory... int getWeeksDelinquentInLastYear() ~~~ 上面的各種取值函數(getter)允許客戶取得各種數據。但有時候一個地點的顧客搬走了,新顧客還沒搬進來,此時這個地點就沒有顧客。由于這種情況有可能發生,所以我們必須保證Customer 的所有用戶都能夠處理「Customer 對象等于null」的情況。下面是一些示例片段: ~~~ Customer customer = site.getCustomer(); BillingPlan plan; if (customer == null) plan = BillingPlan.basic(); else plan = customer.getPlan(); ... String customerName; if (customer == null) customerName = "occupant"; else customerName = customer.getName(); ... int weeksDelinquent; if (customer == null) weeksDelinquent = 0; else weeksDelinquent = customer.getHistory().getWeeksDelinquentInLastYear(); ~~~ 這個系統中可能使用許多Site 和Customer ,它們都必須檢查Customer 對象是否等于null ,而這樣的檢查完全是重復的。看來是使用null object 的時候了。 首先新建一個NullCustomer ,并修改Customer ,使其支持「對象是否為null」的檢查: ~~~ class NullCustomer extends Customer { public boolean isNull() { return true; } } class Customer... public boolean isNull() { return false; } protected Customer() {} //needed by the NullCustomer ~~~ 如果你無法修改Customer ,你可以建立一個新的testing 接口。 如果你喜歡,也可以新建一個接口,昭告大家「這里使用了null object 」: ~~~ interface Nullable { boolean isNull(); } class Customer implements Nullable ~~~ 我還喜歡加入一個factory method,專門用來創建NullCustomer 對象。這樣一來,用戶就不必知道null class 的存在了: ~~~ class Customer... static Customer newNull() { return new NullCustomer(); } ~~~ 接下來的部分稍微有點麻煩。對于所有「返回null」的地方,我都要將它改為「返回null object」,此外我還要把foo==null這樣的檢查替換成foo.isNull()。我發現下列辦法很有用:查找所有『索求Customer 對象」的地方,將它們都加以修改, 使它們不能返回null ,改而返回一個NullCustomer 對象。 ~~~ class Site... Customer getCustomer() { return (_customer == null) ? Customer.newNull(): _customer; } ~~~ 另外,我還要修改所有「使用Customer 對象」的地方,讓它們以isNull() 函數進行檢查,不再使用"== null"”檢查方式。 ~~~ Customer customer = site.getCustomer(); BillingPlan plan; if (customer.isNull()) plan = BillingPlan.basic(); else plan = customer.getPlan(); ... String customerName; if (customer.isNull()) customerName = "occupant"; else customerName = customer.getName(); ... int weeksDelinquent; if (customer.isNull()) weeksDelinquent = 0; else weeksDelinquent = customer.getHistory().getWeeksDelinquentInLastYear(); ~~~ 毫無疑問,這是本項重構中最需要技巧的部分。對于每一個需要替換的「可能等于null」的對象,我都必須找到「它是否等于null」的所有檢查動作,并逐一替換。 如果這個對象被傳播到很多地方,追蹤起來就很困難。上述范例中,我必須找出每一個型別為Customer 的變量,以及它們被使用的地點。很難將這個過程分成更小的步驟。有時候我發現「可能等于null」的對象只在某幾處被用到,那么替換工作比較簡單。但是大多數時候我必須做大量替換工作。還好,撤銷這些替換并不困難,因為我可以不太困難地找出對isNull() 的調用動作,但這畢竟也是很零亂很惱人 的。 這個步驟完成之后,如果編譯和測試都順利通過,我就可以寬心地露出笑容了。接下來的動作比較有趣。到目前為止,使用isNull() 函數尚未帶來任何好處。只有當我把相關行為移到NullCustomer class 中并去除條件式之后,我才能得到切實的利益。我可以逐一將各種行為(函數)移過去。首先從「取得顧客名稱」這個函數開始。此時的客戶端代碼大約如下: ~~~ String customerName; if (customer.isNull()) customerName = "occupant"; else customerName = customer.getName(); ~~~ 首先為NullCustomer 加入一個合適的函數,通過這個函數來取得顧客名稱: ~~~ class NullCustomer... public String getName(){ return "occupant"; } ~~~ 現在,我可以去掉條件代碼了: ~~~ String customerName = customer.getName(); ~~~ 接下來我以相同手法處理其他函數,使它們對相應查詢做出合適的響應。此外我還可以對「修改函數」(modifiers)做適當的處理。于是下面這樣的客戶端程序: ~~~ if (! customer.isNull()) customer.setPlan(BillingPlan.special()); ~~~ 就變成了這樣: ~~~ customer.setPlan(BillingPlan.special()); class NullCustomer... public void setPlan (BillingPlan arg) {} ~~~ 請記住:只有當大多數客戶代碼都要求null object 做出相同響應時,這樣的行為搬移才有意義。注意我說的是「大多數」而不是「所有」。任何用戶如果需要null object 作出不同響應,他仍然可以使用isNull() 函數來測試。只要大多數客戶端都要求null object 做出相同響應,他們就可以調用缺省的null 行為,而你也就受益匪淺了。 上述范例略帶差異的某種情況是,某些客戶端使用Customer 函數的運算結果: ~~~ if (customer.isNull()) weeksDelinquent = 0; else weeksDelinquent = customer.getHistory().getWeeksDelinquentInLastYear(); ~~~ 我可以新建一個NullPaymentHistory class,用以處理這種情況: ~~~ class NullPaymentHistory extends PaymentHistory... int getWeeksDelinquentInLastYear() { return 0; } ~~~ 并修改NullCustomer,讓它返回一個NullPaymentHistory 對象: ~~~ class NullCustomer... public PaymentHistory getHistory() { return PaymentHistory.newNull(); } ~~~ 然后,我同樣可以刪除這一行條件代碼: ~~~ int weeksDelinquent = customer.getHistory().getWeeksDelinquentInLastYear(); ~~~ 你常常可以看到這樣的情況:null objects 會返回其他null objects 。 **范例:另一種做法,Testing Interface** 除了定義isNull() 之外,你也可以建立一個用以檢查「對象是否為null」的接口。 使用這種辦法,必須新建一個Null 接口,其中不定義任何函數: ~~~ interface Null {} ~~~ 然后,讓null object 實現Null 接口: ~~~ class NullCustomer extends Customer implements Null... ~~~ 然后,我就可以用instanceof 操作符檢查對象是否為null : ~~~ aCustomer instanceof Null ~~~ 通常我盡量避免使用instanceof 操作符,但在這種情況下,使用它是沒問題的。而且這種作法還有另一個好處:不需要修改Customer 。這么一來即使無法修改Customer 源碼,我也可以使用null object 。 其他特殊情況 使用本項重構時,你可以有數種不同的null objects ,例如你可以說「沒有顧客」(新建的房子和暫時沒人住的房子)和「不知名顧客」(有人住,但我們不知道是誰) 這兩種情況是不同的。果真如此,你可以針對不同的情況建立不同的null class。有時候null objects 也可以攜帶數據,例如不知名顧客的使用記錄等等,于是我們可以在查出顧客姓名之后將帳單寄給他。 本質上來說,這是一個比Null Object 模式更大的模式:Special Case 模式。所謂special case class(特例類)是某個class 的特殊情況,有著特殊的行為。因此表示「不知名顧客」的UnknowCustomer 和表示「沒有顧客」的NoCustomer 都是Customer 的特例。你經常可以在表示數量的classes 中看到這樣的「特例類」,例如Java 浮點數有「正無窮大」、「負無窮大」和「非數量」(NaN)等特例。special case class(特例類)的價值是:它們可以降低你的「錯誤處理」開銷。例如浮點運算決不會拋出異常。如果你對NaN做浮點運算,結果也會是個NaN。這和「null object 的訪問函數通常返回另一個null object 」是一樣的道理。
                  <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>

                              哎呀哎呀视频在线观看