經過了一個周末足不出戶的草稿與總結,終于能記點東西了。這是第一次,一個人寫,經過認真思索的東西,因為之前,我發現,我一看這家伙--主題,我會幾天就把自己看的東西忘了個精光,實話說,我很認真的,因為有牛人說,依賴程序員的記性,這是非常 糟糕的設計。然后,我相信了。
任何人學某個東西,經歷了一個模糊,甚至頭破血流的撞墻后,都會留下自己的一點痕跡。這痕跡來自你自己對這家伙的一點認識,或深或淺的,縱使有一天,會產生新的,更有深度的,更本質的理解,至少,它是你現在的理解,能夠幫你解釋,甚至解決當前面臨的問題。
主角呢?問的好奇怪,不就在題目中嗎?只是想自己對設計的一點理解記下而已。這或許是我本人對設計模式,當前的,比較深刻的理解。
OK,什么是設計模式?人們在解決一個問題時,都總有這樣那樣的解決方案,其中不乏一種比較好的方案,那么我們對該種方案的抽象,變成了一種設計模式,可以說,設計模式是一種抽象的,而非具體的思想性的東西。GOF里有23種設計模式,其實沒人說,設計模式就只有這幾種,本文主要對其中部分模式進行研究。
在開始之前,我會說明兩點,第一,為了簡單起見,我暫不遵循面向對象設計思想里的某些原則,比如說面向接口編程,面向抽象編程,OCP原則等等。但是,你會疑問,這些原則都沒有,你的設計在哪里,也許那是種很糟糕的設計,但是需要注意到的是,真正項目中,沒有一個優秀的設計者,會不大致遵循這些原則,我說了--簡單起見,把你帶到核心的,本質性的東西,而非帶你去凌亂的叢林中...(很可能,你會象兔子一樣,見到蘿卜,把白菜丟了,我知道,你也不想那樣,可惜沒辦法,程序員的記性就是這樣子),有一天,你會發現設計模式就這么簡單,應用的話,不包含于此。
內容簡介:1.委托的使用..1.1 簡單委托(反向委托) 1.2 雙向委托 1.3 多重委托 1.4 雙向多重委托
2.回調
3.繼承與委托
4 多態
5.主要模式:iterator, adater, chain of responsibility, builder, proxy, decorator, (template)strategy,bridge, state, visitor,observer, command, mediator.
這些模式的編排依照文中出現的順序,并且遵從簡單到復雜。其他模式,我會暫時放過他們。
1 委托
委托,顧名思義,很簡單,有些事,或者你不會或者你懶,你交給了另一個人幫你做,這就是委托。但是,請記住一點,這里的委托有他拓展的意思,我把所有交給別人做事這一過程定義為委托,可能你會在某些書上看到它--你小子放屁,會被你誤倒的,我會很虛心承認這一點的。但是,它對我有效,如果發現它起到了side effect(副作用) on you,或許你得停止這場糟糕的旅行?;氐侥阍瓉淼牡胤?。
看了很多文字,很是糟糕,很多人不喜歡,包括我也是,我是說之前的我,但是現在我會很喜歡一些理論性的東西,然后里面加點實踐的味道進去。真是佩服牛人,已經到了那種境界。既能當牛,又同時是人。
1.1 簡單委托:我們定義兩個類A,B,然后讓B來委托A做事(不給錢的,
),但是會有代價。
Class A{
methodA() {}
methodA'() {}
}
Class B{
A a;
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){ //這不是我們的目標,是一個可協商的客戶,難得碰上這么好的客戶
B b = new B();
b.methodB();
}
代碼很簡單,客戶期待的是b對象,讓他處理,然后,對象b包含的委托對客戶是透明的,也就是說main壓根不知道有A這樣的一個類存在。但是,目的沒達到,很糟糕,出現了NULL Pointer,最糟糕的也是最好解決的錯誤。接下來,得實例話a對象。
方案1:分別改類B和main為
Class B{
A a;
B(A a) { //多了構造函數,給a附一實例
this.a = a;
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){ //多么和藹的客戶,他得根據我們來調整,感動....
(1) A a = new A(); //當然,將(1)(2)寫成B b = new B(new A());會顯得簡潔
(2) B b = new B(a);
b.methodB();
}
待續......
繼續...... 2008-04-15
方案(2): 我們可以在類B中設置一個方法,setA來給B傳遞A實例,去掉剛才的構造函數。
Class B{
A a;
void setA(A a){
this.a = a;
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.setA(new A());
b.methodB();
}
OK,有效果了。然后,我們懷疑這樣的邏輯,客戶跟某公司的經理談判項目的協議簽定問題時,即使該經理把這事交給了下屬去完成,客戶也得去了解那下屬家伙究竟做了什么,客戶知道的太多了。很不好(就象程序員完成某功能時,他得知道很多細節,這樣的編程方式比較危險,為什么會這樣呢?我們得認真考慮這問題,而這也是現在的想法,我們會想著盡量美好的憧憬),我們繼續走下去,下面會有更好點的方案。
方案(3): 我們把對象a的實例化安置到了類B里面,那么這樣的話,依賴的一方必須了解被依賴者,而客戶壓根不懂委托者。
Class B{
A a;
B(A a){
this.a = new A();
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.methodB();
}
如果,用setA函數替代構造函數,會得到方案(4)
方案(4):
Class B{
A a;
void setA(){
this.a = new A();
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.setA();
b.methodB();
}
到此,簡單的委托處理就先告一段落。發現沒有,這四種(其實只有兩種)方案我們在新框架出來之后,我們改了名字---注入對象實例。然后,后來(當然是很早以前),配置文件方式的注入,如spring框架,取了個叫什么依賴注入,反轉控制來著。也就是這東西確實很簡單,沒啥好說的,但是,我又說了,是因為,這跟設計模式有莫大的關系,至少我這么覺得。 然而,我還是得說,以上兩種方案各有好處,在一些簡潔的設計中,我們會習慣于構造類B時同時給被委托者創建實例,一旦,發現被委托者是動態被注入或者被委托者被多次注入時(可以是委托集合),我們會偏向于(2)(4)注入方式。你會發現設計模式大多是這兩種方式的。
方案(5): 反向委托:客戶面向B,而B萬一很忙或不在呢,或者干脆你沒權利聯系B,也就是你沒法實例化一個B,然后,我們可以通過另一種方式來獲取B實例。我稱它為反向委托--可能不恰當,我沒經過太多考慮.
代碼修改如下:在類A中添加getB方法,通過實例A來獲取B實例。
Class A{
B getB() {
return new B(this);
}
methodA() {}
methodA'() {}
}
Class B{
A a;
B(A a){
this.a = a;
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
A a = new A();
B b = a.getB();
b.methodB();
}
如果看過設計模式,細心的你也許會發現了一個這東西很類似于Iterator模式,a是個集合對象,b是個迭代器,可以通過一個迭代器訪問集合類,把數據的操作和數據分開了。當然此處a,b都是普通類,a不是集合,b也不是迭代器。具體的演化過程,接下來的章節將繼續討論。
1.2 雙向委托
看過簡單委托,估計聰明的你猜到了,雙向委托就是互相包含依賴對方。沒錯,確是這樣的。看例子:
Class A{
B b;
A(B b) {
this.b = b;
}
methodA() {}
methodA'() {}
}
Class B{
A a;
B(){
this.a = new A(this);
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.methodB();
}
這是種高度耦合的結構,在他們的世界里,誰都離不開誰,類似于,鄰居關系,咱家沒的東西去找鄰居借,鄰居沒的東西會來找咱家借一樣。當然,這似乎不是很好的設計,但是確實經常碰到,也很有用??梢钥纯疵艚蒈浖_發里的線程控制的模擬實現---active object模式,雖然,它不是經典模式之一。
繼續我們的旅程--2008-04-16
1.3 多重委托
一個類中包含多個被委托者,而這委托是單向的,當然這些被委托者可以是同類型--稱之為委托集合,我們把這種結構定義為多重委托。例子:
Class A{ methodA() {}
methodA'() {}
}
Class A1{ methodA1() {}
methodA1'() {}
}
Class A2{ methodA2() {}
methodA2'() {}
}
Class B{
A a;
A1 a1;
A2 a2;
B(A a, A1 a1, A2 a2){ //此處我們也可以分別定義三個類似setA這樣的方法實例化他們
this.a = a;
this.a1 = a1;
this.a2 = a2;
}
methodB() {
a.methodA();
}
methodB1() {
a1.methodA1();
}
}
void main(){
A a = new A();
A1 a1 = new A1();
A2 a2 = new A2();
B b = new B(a, a1, a2);
b.methodB();
}
就是這種結構,類B委托了3個其他類A,A1,A2。這里我們也可以分別定義三個類似setA這樣的方法實例化他們,同時我們也喜歡這樣做,因為世界是變化的,你也得遵循這樣的約定。這只個表示多重委托的例子,它是個原型,他還未能用于我們的設計中,你會發現這種結構有它的缺點,最簡單之一,他違反了OCP原則,這個類B將無法擴展,擴展的意義在于不應該通過修改原始代碼來完成而應該是通過繼承或委托來完成,想象一下,如果你有一段代碼,每次增加新功能或修改bug時,你都得把類B修改一遍,這樣的結果,很簡單,有一天你會被自己寫的東西淹沒了,你會感覺到一種恐慌,很無助,無奈于客戶的抱怨聲中,同時,也很難保證項目的維護成本。然而這樣的結構確實對我們的設計具有啟發性的意義。我們積極去改寫這樣的結構,使它慢慢處于我們的掌控中,然后,你會有驚奇的發現。
想要擴展性,我們可以讓這些被委托者以集合形式保持住。于是,我們想到觀察者模式,沒錯,就是這樣的一個例子。你還想到什么模式呢?
1.4 雙向多重委托
雙向多重委托是比較復雜的結構,它其實應該是個網狀模型,每一個委托者同時又是被委托者,學過數學的你,自己算算其復雜度。然而,問題在于解決問題時應該盡量簡單化,沒有任何一個司機喜歡,在乘客多時,讓乘客劃拳決勝負后再上車。OK,這樣的結構我們會盡量少用,轉為利用上面多重委托的變形,在類B中保持多個被委托者的同時,讓各個被委托者持有一個委托者,即類B。
2.回調
回調這個詞,你肯定早聽說過了,沒啥好說的,不清楚的百度一下,這里說這個,也啥其他意思。這只是我思考該主題的一個byproduct(副產品),我把對象與對象之間的穿梭行走定義為了回調。
Class A{ methodA(B b) {
b.methodB'();
}
methodA'() {}
}
Class B{
A a;
methodB() {
a.methodA(this);
}
methodB'() {
System.out.println("methodB'");
}
}
void main(){
B b = new B();
b.methodB();
}
這樣就能實現類B的回調,也許你會認為為什么B不直接調用它自己呢?反而繞了一圈呢,相信聰明的你,以后會慢慢理解的,在這里,回調不是主角,同時,如果你想讓這個回調永遠持續下去,那你就去做吧。
3.繼承與委托
講了一堆的委托概念--其實不是這些都不經典理論,只是個人的一點想法而已,該看看繼承了,它已經呆不住了。其實,學過面向對象編程的人都知道,這里只是理理其關系罷了。有一個纂書人,說了一句經典之言,繼承是生離死別,委托是瀟灑分離,我很喜歡這樣的描述,這句話提到了,對象與對象之間的耦合性在這兩者之間的體現,每個人都知道其耦合性應該降到最低層次。然而繼承給我們帶來的麻煩,他糾纏于父類與子類之間,例子還在整理中....所以,牛人又說了,盡量用委托代替繼承。然后,我又相信了。但是,得確認的一點是,繼承也是面向對象的組成之一,我們該權衡之使用。我也自己經常用它。
4 多態 --暫略過
在以后的部分,即將開始真正的內容,它將引領你一起去...,你想去哪里?
posted on 2008-08-13 16:53
zhqh 閱讀(210)
評論(0) 編輯 收藏 所屬分類:
設計模式