论面向组合子程序设计方法 之 南无阿弥陀佛
生活随笔
收集整理的這篇文章主要介紹了
论面向组合子程序设计方法 之 南无阿弥陀佛
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
其實,前面我還忘了提一個非常重要的基本組合子:singleton。?
這里補充提一下:?
Java代碼??class?SingletonComponent?implements?Component{?? ??private?final?Component?c;?? ??private?Object?val;?? ??public?Class?getType();{?? ????return?c.getType();;?? ??}?? ??public?synchronized?Object?create(Dependency?dep);{?? ????if(val!=null);?return?val;?? ????val?=?c.create(dep);;?? ????return?val;?? ??}?? ??public?synchronized?Class?verify(Dependency?dep);{?? ????if(val!=null);?return?val.getClass();;?? ????else?return?c.verify(dep);;?? ??}?? }???
代碼沒什么可說的,就是最簡單的singleton模式。?
用這個組合子,我們可以對任意的Component做singleton。?
下面接著說monad。?
有了bind,很多的功能都可以自然推演出來了。?
比如我們前面用來刁難pico的那個例子,甚至,為了更強調復雜性,我們可以給B和A再另外增加一些參數,這些參數要求從容器解析(畢竟,我們之所以需要容器,就是為了自動解析一些依賴關系,要是全部依賴關系都hard-code,意義就不大了):?
Java代碼??void?A?createA();{?? ??B?b?=?new?B(...);;?? ??return?new?A(b,b,?...);;?? }??
用bind,我們的思路可以是這樣:?
1。用B的構造函數生成一個Component。?
2。這個Component生成一個對象,?
3。這個產生的對象被傳遞給一個對應A的Component當作參數。這一步可以用bind來搞定。?
Java代碼??Component?b_component?=?Components.ctor(B.class);;?? return?new?BoundComponent(b_component,?new?Binder();{?? ??public?Component?bind(Object?b);{?? ????final?Component?arg?=?Components.value(b);;?? ????return?new?WithArgument(?? ??????new?WithArgument(a,?0,?arg);,?? ????1,?arg);;?? ??}?? });;??
Components.value(Object)是我們寫的一個對ValueComponent的封裝靜態函數。?
為了避免總寫冗長的new SomeComponent(...),我們把一些常用的基本Component都寫成名字較短的靜態函數,放在Components類里面。?
這樣,我們可以寫Components.value(obj),而不是new ValueComponent(obj)。?
要是覺得敲鍵盤還是麻煩,你甚至可以創建一個Components對象cc。然后到處用這個對象:?
cc.value(obj)。舒服些了吧??
從上面的例子,我們可以看到,那個直接創建對象的createA函數中的兩個步驟,在我們高階的Component中也被分為兩部。?
而在兩個步驟之間的信息傳遞(那個b變量,從第一個步驟取得,然后在第二個步驟使用),則被用bind操作實現了。?
到這,也許我們該伸伸懶腰了。舒服地往椅子背上一靠,說:“啊。終于干完了!我可以用高階邏輯來模擬任何直接硬編碼創建對象的邏輯了”。?
這話倒也沒錯,有了bind,我們不再被局限于“構造函數注射”,“setter注射”,“靜態工廠注射”等寥寥幾個注射方式;我們甚至可以對所謂的ioc type嗤之以鼻:“什么type1, type2?不過是我們可以處理的無數種情況中的幾種特例而已!”。?
我們可以處理if-else,可以處理循環,遞歸,任何可以直接用java寫出來的對象創建方式,我們都可以在高階邏輯上得到對應的組合版本,只要我們有足夠的原子組合子。(所謂原字組合子,不過是:FunctionComponent, BeanComponent,ValueComponent幾種)?
比如,對應于:?
Java代碼??X?createX();{?? ??A?a?=?A.instance(...);;?? ??if(a.isX(...););{?? ????return?new?X(...);;?? ??}?? ??else{?? ????return?new?Y(a,?...);.getX(...);;?? ??}?? }??
這里,所有的省略號都代表可能需要從容器解析的參數。使用高階Component對象而不是直接調用createX()函數的一個原因,就是我們想要把依賴解析隱藏起來并且集中靈活地配置和管理。?
對此,我們可以寫成:?
Java代碼??Component?a_component?=?Components.static_method(A.class,?"instance");;?? return?new?BoundComponent(a_component,?new?Binder();{?? ??public?Component?bind(final?Object?a);{?? ????final?Component?isx_component?=?Components.method(a,?"isX");;?? ????return?new?BoundComponent(isx_component,?new?Binder();{?? ??????public?Component?bind(Object?isx);{?? ????????final?Boolean?v?=?(Boolean);isx;?? ????????if(v.booleanValue(););{?? ??????????return?Components.ctor(X.class);;?? ????????}?? ????????else{?? ??????????final?Coponent?y_component?=??? ???????????new?WithArgument(Components.ctor(Y.class);,?0,??? ??????????????Components.value(a););;?? ??????????return?new?BoundComponent(y_component,?new?Binder();{?? ????????????public?Component?bind(Object?y);{?? ??????????????return?Components.method(y,?"getX");;?? ????????????}?? ??????????});;?? ???????????? ????????}?? ??????}?? ????});;?? ??}?? });;??
稍微有點繞,如果你到此有點糊涂的話,請重溫一下前面的簡單的bind的例子,只要體會了bind的具體意義,上面的代碼不過是幾層bind的嵌套。?
好,如果你理解了bind,那么應該能夠看懂上面的這段代碼了。它其實就是那個createX函數的嚴格翻譯。?
功能確實很強大了,就是這代碼寫起來這個 煩 啊!對比一下createX和這個高階版本吧。我發現如果我多看幾眼這個所謂的"co"的代碼,我簡直都要吐!如果說createX這個函數的代碼是正常人說話,那么這個高階代碼就是唐僧念經:“南無阿彌陀佛,南無阿彌陀佛,南無阿彌陀佛...”,天啊!?
如果我們真要Combinator-oriented起來,難道要整天寫這種蹩腳代碼?是不是我們吐啊吐的就會習慣了呢??
pico的各個ComponentAdapter其實倒也就是這么寫,可是pico沒有bind,你很少需要寫這么深的嵌套,甚至很少需要寫匿名類。?
如果我們把我們的組件系統比喻作pascal語言的話,pico的那些decorator充其量不過是一個dos的批處理,不,遠不如批處理靈活,應該也就是一個簡單的用戶界面上的幾個按鈕。?
那么有沒有什么辦法來簡化語法呢??
倒是有一個想法:?
1。把Component從接口變成一個抽象類。然后把一些常用的二元組合,比如bind,比如withArgument,withProperty,比如method,ifelse,都放在這個抽象類里面。這樣,?
我們就可以避免寫:?
new SingletonComponent(c),而寫c.singleton()。?
我們就可以避免寫:?
new BoundComponent(c1, ...),而寫:c1.bind(...)。?
可以避免寫:?
new WithArgument(c, 0, arg),而寫:c.withArgument(0, arg)。?
可以避免寫:?
Java代碼??new?BoundComponent(c1,?new?Binder();{?? ??public?Component?bind(Object?obj);{?? ????return?Components.method(obj,?"method");;?? ??}?? });;??
而寫成:?
Java代碼??c1.method("method");;??
可以避免寫:?
Java代碼??new?BoundComponent(c1,?new?Binder();{?? ??public?Component?bind(Object?obj);{?? ????if(((Boolean);obj);.booleanValue(););{?? ??????return?a;?? ????}?? ????else?return?b;?? ??}?? });;??
而寫成:?
Java代碼??c1.ifelse(a,b);;??
等等等等。?
這樣做,從架構上確實有點損害,我們犧牲了“圍繞接口”的原則,而改為圍繞抽象類了。?
但是,從實際效果考慮,我發現它損失的架構上的美感,遠遠比不上它帶來的編碼上的方便程度。誰讓我們用的是java呢,世上沒有十全十美的事情,就湊合吧。?
經過這個改動,上面的對應createX的高階代碼變成:?
Java代碼??Component?a_component?=?Components.static_method(A.class,?"instance");;?? return?a_component.bind(new?Binder();{?? ??public?Component?bind(final?Object?a);{?? ????final?Component?isx_component?=?Components.method(a,?"isX");;?? ????return?isx_component.ifelse(?? ??????Components.ctor(X.class);,?? ??????Components.ctor(Y.class);?? ????????.withArgument(0,?Components.value(a););?? ????????.method("getX");?? ????);;?? ??}?? });;??
稍微好些了。而如果我們不需要給Y的構造函數指定參數,那么效果還會更好。?
比如對?
Java代碼??X?createX();{?? ??A?a?=?A.instance(...);;?? ??if(a.isX(...););{?? ????return?new?X(...);;?? ??}?? ??else{?? ????return?new?Y(...);.getX(...);;?? ??}?? }??
高階代碼會變成:?
Java代碼??Component?a_component?=?Components.static_method(A.class,?"instance");;?? return?a_component.method("isX");.ifelse(?? ????Components.ctor(X.class);,?? ????Components.ctor(Y.class);?? ??????.withArgument(0,?Components.value(a););?? ??????.method("getX");?? );;??
又簡潔了不少。?
當然,說實話,如果我們把情況任意復雜化,比如:?
Java代碼??Y?createY();{?? ??a?=?A.createA(...);;?? ??b?=?B.createB(a,?...);;?? ??c?=?C.createC(a,b,...);;?? ??return?Y.create(a,b,c,...);;?? }??
要對createY寫出高階對應版本,這bind要嵌套三層,代碼無論如何不可能好看了。對此,我們只能聳聳肩說:無能為力了。因為我們這里已經接觸到了java語言的底線。?
值得欣慰的是,至少:?
1。對簡單需求,比如pico能夠處理的那些,我們的語法并不比pico麻煩。?
2。對復雜需求,pico不能處理,而只能通過自己實現ComponentAdapter實現;而我們的co構建出來的系統,在沒有剝奪你自己實現Component的前提下,也提供了采用聲明式的語法來組合的方式。至于是選擇用熟悉的java語法來過程式地自己處理依賴,還是用聲明式的高階邏輯來仍然讓系統處理依賴,則是程序員的自由了。?
我們推薦,除非對非常復雜的需求,還是用聲明式的組合來處理更好。?
寫到這里,不得不嘮叨一些語言了。就象是你也可以在c這個過程語言里面使用一些oo的技巧一樣,我們在java這個oo語言里面是可以使用一些co的技巧的。?
只不過,缺乏語言上的良好支持,讓我們在采用co設計的時候的代價有所增大。如何權衡?是co帶來的缺點(不方便調試,運行效率低,語法麻煩)大,還是它帶來的好處(靈活應對變化,減少代碼數量,方便重用)大,則是一個需要主觀經驗決定的事情了。?
其實,在一個真正支持monad組合子的語言里面,createY會被類似寫成這樣:?
Java代碼??do?? ??a?<-?static_method(A.class,?"createA");;?? ??b?<-?static_method(B.class,?"createB");?? ????.withArgument(0,?a);;?? ??c?<-?static_method(C.class,?"createC");?? ????.withArgument(0,?a);.withArgument(1,?b);;?? ??return?(static_method(Y.class,?"create");?? ????.withArgument(0,a);.withArgument(1,b);.withArgument(2,c);;?? ??);??
所有的Binder匿名類會被自動生成。?
這叫"do-notation",是haskell里面用來方便處理monad組合子的利器。?
在我開發的jaskell語言里面,對do-notation有類似的支持。?
題外話:?
最近,看到老莊設計的DJ里面說要支持co。我覺得,如果僅僅象java這樣的所謂“支持”,那就和用C的函數指針號稱支持OO一樣無趣了。?
一個可以說得上對co有支持的語言,即使不直接支持do-notation,也應該把寫匿名類的代價降到和一個lamda函數相接近的程度。?
即使我不能寫?
Java代碼??a?<-?createA?? b?<-?createB?a??
也要能夠寫成:?
Java代碼??createA?>>=?\a->createB?a??
這里補充提一下:?
Java代碼??
代碼沒什么可說的,就是最簡單的singleton模式。?
用這個組合子,我們可以對任意的Component做singleton。?
下面接著說monad。?
有了bind,很多的功能都可以自然推演出來了。?
比如我們前面用來刁難pico的那個例子,甚至,為了更強調復雜性,我們可以給B和A再另外增加一些參數,這些參數要求從容器解析(畢竟,我們之所以需要容器,就是為了自動解析一些依賴關系,要是全部依賴關系都hard-code,意義就不大了):?
Java代碼??
用bind,我們的思路可以是這樣:?
1。用B的構造函數生成一個Component。?
2。這個Component生成一個對象,?
3。這個產生的對象被傳遞給一個對應A的Component當作參數。這一步可以用bind來搞定。?
Java代碼??
Components.value(Object)是我們寫的一個對ValueComponent的封裝靜態函數。?
為了避免總寫冗長的new SomeComponent(...),我們把一些常用的基本Component都寫成名字較短的靜態函數,放在Components類里面。?
這樣,我們可以寫Components.value(obj),而不是new ValueComponent(obj)。?
要是覺得敲鍵盤還是麻煩,你甚至可以創建一個Components對象cc。然后到處用這個對象:?
cc.value(obj)。舒服些了吧??
從上面的例子,我們可以看到,那個直接創建對象的createA函數中的兩個步驟,在我們高階的Component中也被分為兩部。?
而在兩個步驟之間的信息傳遞(那個b變量,從第一個步驟取得,然后在第二個步驟使用),則被用bind操作實現了。?
到這,也許我們該伸伸懶腰了。舒服地往椅子背上一靠,說:“啊。終于干完了!我可以用高階邏輯來模擬任何直接硬編碼創建對象的邏輯了”。?
這話倒也沒錯,有了bind,我們不再被局限于“構造函數注射”,“setter注射”,“靜態工廠注射”等寥寥幾個注射方式;我們甚至可以對所謂的ioc type嗤之以鼻:“什么type1, type2?不過是我們可以處理的無數種情況中的幾種特例而已!”。?
我們可以處理if-else,可以處理循環,遞歸,任何可以直接用java寫出來的對象創建方式,我們都可以在高階邏輯上得到對應的組合版本,只要我們有足夠的原子組合子。(所謂原字組合子,不過是:FunctionComponent, BeanComponent,ValueComponent幾種)?
比如,對應于:?
Java代碼??
這里,所有的省略號都代表可能需要從容器解析的參數。使用高階Component對象而不是直接調用createX()函數的一個原因,就是我們想要把依賴解析隱藏起來并且集中靈活地配置和管理。?
對此,我們可以寫成:?
Java代碼??
稍微有點繞,如果你到此有點糊涂的話,請重溫一下前面的簡單的bind的例子,只要體會了bind的具體意義,上面的代碼不過是幾層bind的嵌套。?
好,如果你理解了bind,那么應該能夠看懂上面的這段代碼了。它其實就是那個createX函數的嚴格翻譯。?
功能確實很強大了,就是這代碼寫起來這個 煩 啊!對比一下createX和這個高階版本吧。我發現如果我多看幾眼這個所謂的"co"的代碼,我簡直都要吐!如果說createX這個函數的代碼是正常人說話,那么這個高階代碼就是唐僧念經:“南無阿彌陀佛,南無阿彌陀佛,南無阿彌陀佛...”,天啊!?
如果我們真要Combinator-oriented起來,難道要整天寫這種蹩腳代碼?是不是我們吐啊吐的就會習慣了呢??
pico的各個ComponentAdapter其實倒也就是這么寫,可是pico沒有bind,你很少需要寫這么深的嵌套,甚至很少需要寫匿名類。?
如果我們把我們的組件系統比喻作pascal語言的話,pico的那些decorator充其量不過是一個dos的批處理,不,遠不如批處理靈活,應該也就是一個簡單的用戶界面上的幾個按鈕。?
那么有沒有什么辦法來簡化語法呢??
倒是有一個想法:?
1。把Component從接口變成一個抽象類。然后把一些常用的二元組合,比如bind,比如withArgument,withProperty,比如method,ifelse,都放在這個抽象類里面。這樣,?
我們就可以避免寫:?
new SingletonComponent(c),而寫c.singleton()。?
我們就可以避免寫:?
new BoundComponent(c1, ...),而寫:c1.bind(...)。?
可以避免寫:?
new WithArgument(c, 0, arg),而寫:c.withArgument(0, arg)。?
可以避免寫:?
Java代碼??
而寫成:?
Java代碼??
可以避免寫:?
Java代碼??
而寫成:?
Java代碼??
等等等等。?
這樣做,從架構上確實有點損害,我們犧牲了“圍繞接口”的原則,而改為圍繞抽象類了。?
但是,從實際效果考慮,我發現它損失的架構上的美感,遠遠比不上它帶來的編碼上的方便程度。誰讓我們用的是java呢,世上沒有十全十美的事情,就湊合吧。?
經過這個改動,上面的對應createX的高階代碼變成:?
Java代碼??
稍微好些了。而如果我們不需要給Y的構造函數指定參數,那么效果還會更好。?
比如對?
Java代碼??
高階代碼會變成:?
Java代碼??
又簡潔了不少。?
當然,說實話,如果我們把情況任意復雜化,比如:?
Java代碼??
要對createY寫出高階對應版本,這bind要嵌套三層,代碼無論如何不可能好看了。對此,我們只能聳聳肩說:無能為力了。因為我們這里已經接觸到了java語言的底線。?
值得欣慰的是,至少:?
1。對簡單需求,比如pico能夠處理的那些,我們的語法并不比pico麻煩。?
2。對復雜需求,pico不能處理,而只能通過自己實現ComponentAdapter實現;而我們的co構建出來的系統,在沒有剝奪你自己實現Component的前提下,也提供了采用聲明式的語法來組合的方式。至于是選擇用熟悉的java語法來過程式地自己處理依賴,還是用聲明式的高階邏輯來仍然讓系統處理依賴,則是程序員的自由了。?
我們推薦,除非對非常復雜的需求,還是用聲明式的組合來處理更好。?
寫到這里,不得不嘮叨一些語言了。就象是你也可以在c這個過程語言里面使用一些oo的技巧一樣,我們在java這個oo語言里面是可以使用一些co的技巧的。?
只不過,缺乏語言上的良好支持,讓我們在采用co設計的時候的代價有所增大。如何權衡?是co帶來的缺點(不方便調試,運行效率低,語法麻煩)大,還是它帶來的好處(靈活應對變化,減少代碼數量,方便重用)大,則是一個需要主觀經驗決定的事情了。?
其實,在一個真正支持monad組合子的語言里面,createY會被類似寫成這樣:?
Java代碼??
所有的Binder匿名類會被自動生成。?
這叫"do-notation",是haskell里面用來方便處理monad組合子的利器。?
在我開發的jaskell語言里面,對do-notation有類似的支持。?
題外話:?
最近,看到老莊設計的DJ里面說要支持co。我覺得,如果僅僅象java這樣的所謂“支持”,那就和用C的函數指針號稱支持OO一樣無趣了。?
一個可以說得上對co有支持的語言,即使不直接支持do-notation,也應該把寫匿名類的代價降到和一個lamda函數相接近的程度。?
即使我不能寫?
Java代碼??
也要能夠寫成:?
Java代碼??
組合并不僅僅是幾個簡單的decorator套起來。真正復雜的co里,不同組合子之間是需要通過bind來通信的。而組合子之間的通信能力才是co強大的根源。
from:?http://ajoo.iteye.com/blog/23326
總結
以上是生活随笔為你收集整理的论面向组合子程序设计方法 之 南无阿弥陀佛的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论面向组合子程序设计方法 之 重构
- 下一篇: 论面向组合子程序设计方法 之 重构2