Java中的模式
世上一直有一個神話:設計可以并且應該獨立于實現的細節,設計通常被看作是一個抽象的概念而實現是一個代碼的具體實例。如果我們堅信"設計是一個富有創造性和目的性的活動:為某一個目標而精心制定的結構的概念,",一個結構如果不能夠說明它的環境,或者不能與環境協作,那么這個結構就不適合這一目標。環境中包括目標平臺--語言、工具、庫、中間件(middleware),等。還有它的功能性和非功能性的單元。
我們會認為在不知道地形布局的時候設計房屋,或者在不清楚使用的道材料的時候建造摩天大廈是不合理的事情。我們將線程、分布這類概念看作為小的編碼的細節的看法無疑是在設計中導致浪費精力(時間和金錢)的導火索,最終我們發現的是理論與實踐的差距在實踐中要比在理論中還大。雖然在一些情況下一個高層次設計的某部分可以在許多技術下保持不變,但是更多的情況是我們需要親自來補足這個圓圈,允許(甚至鼓勵)細節和實際的信息來影響并告知系統的結構。
模式(Patterns)的作用就是獲取這些結構上的信息。它們可以描述--預見性的或回顧性的--設計和設計的原理,講述從問題到解決,說明環境,獲取工作的動力以及應此產生的結果。這里,我將集中講述兩個模式--Command-Query Separation和Command Method--為一個類接口中的方法分配任務,考察他們如何互相作用并影響并發的、分布的和有序的環境以及本地執行。
接口設計。顧名思義,接口提供了不同系統之間或者系統不同組件之間的界定。在軟件中,接口提供了一個屏障,從而從實現中分離了目標,從具體中分離了概念,從作者中分離了用戶。在Java中,有許多接口的概念:public部分為潛在的用戶提供了類和方法的接口,protected部分為它的子類(subclass)以及周圍的包提供了一個接口;一個包有一個公用的部分;反射(Reflection)是另外一種提供、使用對象方法接口的機制。
約束及供給。站在用戶對作者的角度,一個接口建立并命名了一個目的模型的使用方法。類接口中的方法提供了一種特殊的使用方法。是這些約束--編譯時的類型系統,運行是的異常機制及返回值--使得類作者的目的得以體現和加強。在這方面最簡單的例子是對封裝的意義的理解:私有化可以保證類用戶只可以通過類的公用方法接口來操作信息和行為。
然而,對于封裝來說,遠不止數據私有那么簡單。在設計中,封裝往往會涉及到自我包含(self-containment)。一個需要你知道如何調用一個方法(e.g."在一個線程的環境中,在一個方法調用后調用另一個方法,你必須明確地同步對象")的類的封裝就不如將所有這些全部包含并隱藏的類(e.g."這個類是thread-safe的")好。前一個設計存在著設計的漏洞,它的許多限定條件是模糊的而不是經過加強的。這就把責任推給了用戶而不是讓類提供者做這些工作來完成類的設計,并且,這是不可避免的漏洞百出。
在這種情況下,供給(affordances)描述了使用的可行性和不可行性。
術語供給(affordances)指事物的被感知的真實的屬性,首先,這些屬性可以決定事物的使用的可能方法。一個椅子可以用來支撐其他東西,所以,可以坐人。一個椅子照樣可以搬運(carried)。玻璃可以透光,也可以被打碎……
供給提供了對事物操作的線索,板狀物可以壓、柄狀物可以旋轉,溝狀物可以插入東西。球狀物可以扔或者反彈。當使用了供給的優勢后,用戶可以只通過看便確定該做什么:沒有圖、沒有標簽也沒有說明。復雜的事物可能會需要一些解釋,但是簡單的事物不應該這樣。當簡單的東西也需要用圖片、標簽來說明的時候,設計就是失敗的。
類設計者的一個職責便是在接口中減小約束與供給之間的隔閡(gap),匹配目標以及一定程度上的自由度,盡可能減小錯誤使用的可能。
對環境敏感的設計。在空間或者時間上分離方法的執行--例如,線程,遠程方法調用,消息隊列--能夠對設計的正確性和效率產生意義深遠的影響。這種分離帶來的結果是不可忽視的:并發引入了不確定性和環境選擇的開銷;分布引入了錯誤的和不斷增加的回程的調用開銷。這些是設計的問題,而不是修改bug那樣簡單。
無論是在何種情況下,結果都是將會阻礙所有權風格的程序設計(Property-Style Programming)--當一個接口主要由set和get方法組成的時候,每個方法都相應的直接指向私有區域。這樣的類的封裝很差(意思是毫無遮掩)。接口中的域訪問器(Field accessors)通常是不會提供信息的:他們在對象的使用中不能通訊、簡單化和抽象化,這通常會導致冗長并易出現錯誤的代碼。所有權風格的程序設計在短時間內不是一個大的活動。分布和并行通過引入了正確性和嚴重的性能開銷放大了這些格式上實踐的問題。
我們會認為在不知道地形布局的時候設計房屋,或者在不清楚使用的道材料的時候建造摩天大廈是不合理的事情。我們將線程、分布這類概念看作為小的編碼的細節的看法無疑是在設計中導致浪費精力(時間和金錢)的導火索,最終我們發現的是理論與實踐的差距在實踐中要比在理論中還大。雖然在一些情況下一個高層次設計的某部分可以在許多技術下保持不變,但是更多的情況是我們需要親自來補足這個圓圈,允許(甚至鼓勵)細節和實際的信息來影響并告知系統的結構。
模式(Patterns)的作用就是獲取這些結構上的信息。它們可以描述--預見性的或回顧性的--設計和設計的原理,講述從問題到解決,說明環境,獲取工作的動力以及應此產生的結果。這里,我將集中講述兩個模式--Command-Query Separation和Command Method--為一個類接口中的方法分配任務,考察他們如何互相作用并影響并發的、分布的和有序的環境以及本地執行。
接口設計。顧名思義,接口提供了不同系統之間或者系統不同組件之間的界定。在軟件中,接口提供了一個屏障,從而從實現中分離了目標,從具體中分離了概念,從作者中分離了用戶。在Java中,有許多接口的概念:public部分為潛在的用戶提供了類和方法的接口,protected部分為它的子類(subclass)以及周圍的包提供了一個接口;一個包有一個公用的部分;反射(Reflection)是另外一種提供、使用對象方法接口的機制。
約束及供給。站在用戶對作者的角度,一個接口建立并命名了一個目的模型的使用方法。類接口中的方法提供了一種特殊的使用方法。是這些約束--編譯時的類型系統,運行是的異常機制及返回值--使得類作者的目的得以體現和加強。在這方面最簡單的例子是對封裝的意義的理解:私有化可以保證類用戶只可以通過類的公用方法接口來操作信息和行為。
然而,對于封裝來說,遠不止數據私有那么簡單。在設計中,封裝往往會涉及到自我包含(self-containment)。一個需要你知道如何調用一個方法(e.g."在一個線程的環境中,在一個方法調用后調用另一個方法,你必須明確地同步對象")的類的封裝就不如將所有這些全部包含并隱藏的類(e.g."這個類是thread-safe的")好。前一個設計存在著設計的漏洞,它的許多限定條件是模糊的而不是經過加強的。這就把責任推給了用戶而不是讓類提供者做這些工作來完成類的設計,并且,這是不可避免的漏洞百出。
在這種情況下,供給(affordances)描述了使用的可行性和不可行性。
術語供給(affordances)指事物的被感知的真實的屬性,首先,這些屬性可以決定事物的使用的可能方法。一個椅子可以用來支撐其他東西,所以,可以坐人。一個椅子照樣可以搬運(carried)。玻璃可以透光,也可以被打碎……
供給提供了對事物操作的線索,板狀物可以壓、柄狀物可以旋轉,溝狀物可以插入東西。球狀物可以扔或者反彈。當使用了供給的優勢后,用戶可以只通過看便確定該做什么:沒有圖、沒有標簽也沒有說明。復雜的事物可能會需要一些解釋,但是簡單的事物不應該這樣。當簡單的東西也需要用圖片、標簽來說明的時候,設計就是失敗的。
類設計者的一個職責便是在接口中減小約束與供給之間的隔閡(gap),匹配目標以及一定程度上的自由度,盡可能減小錯誤使用的可能。
對環境敏感的設計。在空間或者時間上分離方法的執行--例如,線程,遠程方法調用,消息隊列--能夠對設計的正確性和效率產生意義深遠的影響。這種分離帶來的結果是不可忽視的:并發引入了不確定性和環境選擇的開銷;分布引入了錯誤的和不斷增加的回程的調用開銷。這些是設計的問題,而不是修改bug那樣簡單。
無論是在何種情況下,結果都是將會阻礙所有權風格的程序設計(Property-Style Programming)--當一個接口主要由set和get方法組成的時候,每個方法都相應的直接指向私有區域。這樣的類的封裝很差(意思是毫無遮掩)。接口中的域訪問器(Field accessors)通常是不會提供信息的:他們在對象的使用中不能通訊、簡單化和抽象化,這通常會導致冗長并易出現錯誤的代碼。所有權風格的程序設計在短時間內不是一個大的活動。分布和并行通過引入了正確性和嚴重的性能開銷放大了這些格式上實踐的問題。