最近更新: 2009-12-03

與 metavige 和 alexchen 對話 Java 語言

所以當然這個 Java 老語言來說,自然沒得比了~

目前的開發方式或者是環境變遷,強調的是快速開發,以往那種慢工出細活的方式已經有點不合時宜了

metavige

有趣的是,Java一點都不老。我在 從中介編程與反射能力來談 Java 語言 列出十年世代的程式語言列表,Java 名列其中。它比Python 年輕3歲,跟Ruby, PHP, JavaScript 同年發佈。單純看語言的特性,Smalltalk這個40歲的程式語言都比 Java 靈活,這才是 Java 令我們老輩程序員驚奇的事。

我現在還記得 Java 十年前剛出現時的宣傳詞中包含「簡單易學」,那當然是跟 C 語言比。然而現在來看,似乎愈來愈不像那麼一回事了。我不認為 Java 讓我們「慢工出細活」,我覺得它帶來的是「冗餘的複雜性」。就算以 C++ 的觀點來看 Java 程式碼,我仍覺得 Java 程式碼有許多不必要的複雜度。Java 把類別變成施加在程序員身上的束縛,而是不是幫助我們進行抽象化資料處理的工具。

其實我重頭看你的作法,我直覺就會想到用 Strategy Pattern 的方式做 用繼承的方式,而不是委託。

不一樣的語言,就會有不同的概念。因為 Generic Type 本來就不是在做 Template 的 是以 OO 的觀點來做到不 DRY ~ 這是我自己的理解。

metavige

Java 專家對泛型的理解是這樣的:

專家一致同意 Java 實踐泛型並不足取。這種說法往往讓聽眾大吃一驚。

Java 泛型規格導入許多語法以解決小問題,卻沒有對 JVM 做出對等的改變。泛型的語法是新的,實踐此語法的方式更是糟糕。越來越多專家宣稱動態分型(dynamic typing)可以讓應用更簡單、編程更有效率, Java 的編程員卻得走回頭路,學習使用更強化的靜態型態。

超越Java(Beyond Java),Bruce A. Tate

我個人認為,所謂「更強化的靜態型態」是跟 C++ 樣板相比, Java 泛型比起 C++ 樣板是在走回頭路。就我到目前為止的 Java 使用經驗來看,我幾乎以為泛型只是 Java 專門用來重新設計容器類別的特殊語法。在那以外的場所,你大概不會想用泛型來重構你的程式碼。

metavige 就說會想用 Strategy Pattern 來重構我在 從 C++ Template 到 Java Generic,一步一步來 舉的例子,而不會用泛型。但是泛型難道不是用來處理這個問題的直覺想法嗎? Java 沒有足夠理由說服我們不要用泛型來做,但是用泛型來做... 呃,似乎更困難。

老闆也不一定願意去使用其他的語言在新系統的開發,因為當我用了一個新的語言即使是在同一個JVM的平台上run,會造成這個系統後續的人維護不是那麼容易,對於接手的人而言,勢必他也要會我新使用的語言,所以老闆就打回票了

遺憾的在這樣子的環境下,我與其不斷的抱怨這個語言不好寫,我就只能好好考量該有什麼方式把這個東西寫好瞜,並且把我的心思"暫時"的全部都放在JAVA上面以求在這圈子不被淘汰,不知道石頭大是根本沒這個煩惱,還是有很好的方式可以克服這樣子的問題?

alexchen

多學幾種程式語言之後,再回頭去寫 Java ,自然就會寫的好。因為你的眼界開了,思想靈活了,會從更抽象的觀點來編程。

當你習慣動態語言靈活的反射、抽象的型別與中介編程能力時,你也會像我一樣,不自覺地在 Java 中也想這麼做。然而,你可能也會像我一樣失望。

很多人都說用新的語言會造成接手的人維護困難。但就我觀察圍繞著 Java 的編程環境,這說法就跟拔獅子的毛可以找回失去的頭髮一樣,沒有事實根據。

找兩個 Java 程度一樣的大學畢業生,同樣都沒學過任何 J2EE 框架,連 servlet 是啥都不知道 (我想這不太難找,因為大學課程通常都不會教特定的框架)。然後一個去學 spring + hibernate + xxx(一堆框架),一個去學 Ruby on Rails 或 PHP 。再要他們開發同樣的 web 應用。我敢打賭在相同的客戶要求品質下,採用 Ruby on Rails 或 PHP 的那個人會更快完工。

因為 Java 語言不是維護的門檻,那一堆框架才是。愈來愈多的框架,把 Java 程式的維護門檻抬到天上去了。我不認為任何一個會 Java 但不會那些框架的大學畢業生接手維護前人留下的 J2EE 開發的軟體會比接手 Ruby on Rails 或 PHP 開發的軟體快。

我以前待的公司接一件案子,因為客戶用 IBM 的機器,而且對 WebSphere 有興趣。所以中間跟 IBM 接洽過,我問 IBM 的人員,培養一組會 Java 但不懂 WebSphere 的程序員到他們有開發能力的時間大概要多久? IBM 的人回答我: 至少半年才合格 (其實我每年都參加 IBM developerWorks Live ,對這個時程心裡早就有底了)。

哇,半年耶。台灣哪間軟體公司會花時間去培養一組只會 Java 的畢業生搞懂這麼多框架? 同樣讓這些人去學 Ruby on Rails 或 PHP,半年下來都變老手了。

國外早在三、五年前就開始改弦易轍了,連 Spring 也早早導入 Groovy & Grails 。其實我們一直都是在多語言的環境下做事,例如傳統的 Java 編程人員也會用 SQL , HQL 查詢資料庫 (我幾乎沒看過 Java 程式使用像 DBM, GDBM 這類的雜湊表式資料庫,這類資料庫連 SQL 都不提供,真正是用程式語言在處理資料);用 Ant 語法處理軟體自動化建置。unix文化的編程人員還會用 shell script 將雜七雜八的例行工作自動化。

我還在一個 J2EE 的 Web 應用專案中,用 Ruby on Rails 去維護資料庫遷移(db migration)、寫 web 應用的測試案例。 Java 已經夠令我惱火了,連測試案例也用 Java 去寫就太自虐了。

如果哪天有人突發奇想,用 Ruby 寫了一整套 Java 程式碼轉譯器,可以用 Ruby 寫程式產出一整個 Java 專案的 code (Hibernate 都可以包起整個 SQL 的產出工作了),然後編譯測試後部署給客戶。客戶也只會覺得「這間公司的 Java 專案做得真快」。客戶只關心你的軟體是不是在 JVM 上跑,更準確地說,他們只要看到一堆 .jar, .war 檔就滿意了,並不關心你在開發過程中用了哪些語言與工具。

我建議你像我一樣,在不必交付給客戶的地方,儘可能用適當的語言來解決問題。 Ford 對此有一個很好的說法。

Groovy 讓你可輕易替 Java 程式碼撰寫單元測試,這是將 Groovy 偷偷帶進保守機構的好藉口 (畢竟,對程式碼進行測試是基礎架構的一部份,並沒有要部署至成品環境中,所以,誰會在乎你使用什麼開源碼程式庫,對吧?) 事實上,我極力倡導,有非開發人員在旁邊時,不要直接稱呼 Groovy 其名。我寧願稱其為 Enterprise Business Exceution Language (使用縮寫 ebXl,因為經理人覺得有大寫字母 X 的縮寫都很迷人)。

《程式設計師提升生產力秘笈》,Ford

我不煩惱寫不好 Java 程式,我只煩惱 Java 沒有能力實現我想表達的設計。碰到這種時候,可以找些事情發洩壓力、緩解情緒。像我在 blog 上發文痛批,就是一種舒壓手段。提供給你參考。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/10914229.html

樂多舊回應
未留名 (#comment-20142345)
Fri, 04 Dec 2009 09:32:11 +0800
現在也是用grails groovy 讓自己的工作變輕鬆^^
未留名 (#comment-20142409)
Fri, 04 Dec 2009 09:58:28 +0800
看完整篇文章...我只對「用適當的語言來解決問題」這句有共嗚...

我不會拿石頭來砸自己腳了之後,再來抱怨這顆石頭怎麼這麼硬 :)
inpines@gmail.com(同人) (#comment-20142899)
Fri, 04 Dec 2009 12:12:35 +0800
我幾乎以為泛型只是 Java 專門用來重新設計容器類別的特殊語法。在那以外的場所,你大概不會想用泛型來重構你的程式碼。


未必,這篇文章或許可以給你一點方向。至於 Bruce 的觀點,他一向給人有批評 Java 不遺餘力的印象。

語言這種東西,很多人常很主觀,但我認為不必要如此。重點在於明白它們的限制是什麼,如何在限制之下,活用特性來解決你的問題。除此之外,假設某種設計情境說某種語言不好的批評,其實只會引來不必要的爭論而已。

BTW,提醒一下,泛型與OO的多型是兩回事,不應該拿來相提並論。
inpines@gmail.com(同人) (#comment-20142927)
Fri, 04 Dec 2009 12:19:44 +0800
靜態型態比較好?還是動態型態?這又是一個爭論不休而不會有答案的問題了。重點是你要的東西是什麼,你要的東西別人不見得想要,反之亦然,各取所需吧!
未留名 (#comment-20143983)
Fri, 04 Dec 2009 16:28:45 +0800
恩~
或許,我對這種理論性的東西,其實並不太知道要怎樣解釋
因為我不算是有正統的理論

對我來說,所有的語言有的特性,要怎樣在適當地時機使用,才是重要的
因為就光同一種語言,就會有很多種方式來解決同一樣問題
並沒有孰是孰非,就如同人說的,這件事情是很主觀的
寫出來的程式好維護,對我來說才是重點
不要寫出來的東西,沒什麼人看得懂,只有寫得人看得懂
這樣就不算是好程式
因為我本身所在的公司比較強調的是團隊合作而不是需要一位超級英雄

謝謝石頭成、alexchen以及同人給我不一樣的想法
coolpopy7022@gmail.com(alexchen) (#comment-20144841)
Fri, 04 Dec 2009 19:05:25 +0800
基本上石頭大說的內容我都贊同,由石頭大的文章可以觀察石頭大的工作性質為專案性質的,交給客戶就沒事了,maintain是客戶的事情,跟開發人員無關,而且可以新舊系統切割得很乾淨。

在之前的討論串中,石頭大少轉貼了我的一句話,然後就回答了我的問題,但我覺得那是我整篇回文的重中之重,因此不厭其煩的再次請教,就是當legacy system 已經存在的時候,根據石頭大的事實,學php的確比java+一對囉哩囉嗦的框架好很多,可能一個月內就可以上手開發新的東西,但問題的重點在於,老系統可能寫的不那麼美好,必定需要已經使用了"java+一對囉哩囉嗦的框架"的人來維護,這種情況下,老闆不會願意讓人去學除了"java+一對囉哩囉嗦的框架"以外的東西,也不希望加入"java+一對囉哩囉嗦的框架"以外的東西,即使php再好學,對已經使用的很爛的"java+一對囉哩囉嗦的框架"的系統而言,還是增加了整個系統的複雜度,所以老闆還是會說"NO!!我花六個月的時間讓一個人才能上手為護這個系統已經很煩了很討厭了,還要額外的讓他在花一個月的時間讓他能夠會php!?"

學新東西不是不好,觀念的成長也都正確,不過就覺得這是個兩難的問題,因此才請教是否有好的方法去面對。
未留名 (#comment-20145975)
Sat, 05 Dec 2009 02:09:17 +0800
jaceju 說: 「我不會拿石頭來砸自己腳了之後,再來抱怨這顆石頭怎麼這麼硬 :)」

這個石頭也不是我自己想搬的,抱怨一下不為過吧。

同人 說:「語言這種東西,很多人常很主觀,但我認為不必要如此。重點在於明白它們的限制是什麼,如何在限制之下,活用特性來解決你的問題。除此之外,假設某種設計情境說某種語言不好的批評,其實只會引來不必要的爭論而已。BTW,提醒一下,泛型與OO的多型是兩回事,不應該拿來相提並論。」

我完全沒談多型。在上一篇文章的例子中,我一開頭就說「有 N, M, S 三個類別,這三個類別沒有繼承關係」,他們之間沒有任何關係。在改寫過程中我還提到為了配合 Java 的泛型特性,我被迫讓那三個類別之間出現了一個介面的實作關係。按我原始想法,用泛型解決演算法重複問題時,依然保持那三個類別之間無關性。這並不是多型的問題。

從結果來看,因為 Java 的泛型不能保持那三個類別的無關性,按我的需求來評分,當然只能"很勉強地"給它60分。

基本上,我始終是就一事來評一事。 Java 在什麼地方有缺陷,我就提出來講講。常看這 blog 的人,應該也常看我在唸其他語言,我唸過 C++ 樣板很難搞,也唸過 PHP 不夠動態之類。一切只是本於實事求是的探討精神罷了。

DANNY 說:現在也是用grails groovy 讓自己的工作變輕鬆^^ 」。

alexchen 說:「石頭大的工作性質為專案性質的,交給客戶就沒事了,maintain是客戶的事情,跟開發人員無關,而且可以新舊系統切割得很乾淨。學新東西不是不好,觀念的成長也都正確,不過就覺得這是個兩難的問題,因此才請教是否有好的方法去面對。 」

並非如此。事實上是,大多數的客戶根本不會自己 maintain 。所以我們交付給客戶的軟體最後還是會回到我們的手上。正因為如此,我才覺得在開發系統時,不必考慮什麼框架相容的問題,應儘可能提早選擇學習曲線短的框架。在 Java 世界也是一步步地從 EJB 走到 Spring 。現在也該想想 Spring 的下一步是什麼吧?不想跨太大步的,可以考慮 Groovy and Grails ,這是 Spring 欽定接班人。就像 DANNY 做的。
alexchen 可能還沒去看 Grails 是什麼?簡單地說,就是 Groovy 語言開發基於 Spring + Hibernate 框架的軟體。就血統而言,Groovy 可以直接使用現有的 Java 框架。
ps.這也是我個人目前苦惱的一件事,我偏好 JRuby ,但是用 JRuby 搭 Spring + Hibernate 還是挺不便的,不如 Groovy 直接。

再者,要新進人員開始接觸新框架並不表示舊系統就完全不管了。難道舊系統回來維護時,我們就不能用新框架、新工具抽換掉舊系統中的元件嗎?就算我眼中的 Java 再怎麼爛,我相信還是做得到這種基本的元件抽換動作。

例如舊系統用 JDBC ,我們可以在修改時用 Hibernate 來完成要修改的部份。注意,只在要修改的部份改用 Hibernate 。我可沒叫你一次就把整個舊系統的資料庫層換掉。

此外,我以前在和其他人聊 Agile method/XP 的結對編程 (pair programming) 時(請見敏捷方法實務研討會會後筆記1 - 溝通與 Pair programming), Thinker 兄就提到「師徒制度」(請看 軟體工程的黑手,這個站點可能關閉了,只能請你看google的頁庫存檔)。我覺得這就是一種揉合新舊人員技術的好方法。

megative 說: 「因為我本身所在的公司比較強調的是團隊合作而不是需要一位超級英雄」

我倒覺得台灣程序員的英雄主義色彩太重。我找工作時,每一間公司面試都在問「你能不能獨立作業」、「你一個人能不能負責這事」;每次聽到這問題,我都覺得很無奈。我待過三間軟體開發公司,沒有一間實現 Agile method/XP 的結對編程制度。我認為這造成程序員彼此之間太少發生「文化衝突」,缺少一個對照組來發掘自己熟悉的框架與工具之優缺點。

我不是英雄,我只是一個太過老實的程序員。如果可以的話,我希望自己可以像我的網頁背景圖中的蓑笠老翁,身在雲深不知處。


inpines@gmail.com(同人) (#comment-20146497)
Sat, 05 Dec 2009 12:08:29 +0800
我說多型不是對石頭成說的,而是對評論寫到design pattern的朋友說的,多型與泛型解決的問題不一樣,不該拿來比較.原諒我沒說清楚.

仔細看了一下石頭成的程式,我想三個類別完全無關或是有關之辨,問題在於界面上.印象中,我知道石頭成好像不大喜歡 java 的 interface 這個東西,所以你會覺得類別耦合介面不好,但在 java 的 oo 觀點來看,這種結構是很正常的,我稱為它這是靜態型態的務虛的設計觀點.

但 c++ 與 java 的不同,是它不是純 oo 的程式語言,所以他不需要以 Integer 來取代 int,而且 java 捨棄了它所特有的指標,卻是 c++ STL 以 iterator 擴展其義涵而漂亮地結合物件與非物件.但實際上來說,c++ 的 template 是複製的範本,所以它是靜態型態的務實設計觀點.

你或許對我說的虛實不見得認同,但卻不能否認 c++ 與 java 的典範不一樣.Java 認為一切都是物件,c++ 則接受有非 oo 的事實.所以 Java 碰到原生型別,就不得不出現一些怪怪的東西了.

其實我一直偏好 c++ 語言,覺得它真的是簡潔的語言,但 java 其實也不差,可能你認為不好的地方,正是我覺得也還不錯的另一種設計觀點.任何事總有兩面觀點,因價值偏好固定限縮它反而是一種不必要的執著.
未留名 (#comment-20147139)
Sat, 05 Dec 2009 19:37:57 +0800
to 同人:
我要那三個類別保持無關性,是因為那三個類別「本質上」就是無關的。並非因為我個人不喜歡介面。

就好像 Integer 跟 String ,它們就是沒有關係。Integer::Number::Object; String::Object,它們的關係只是 Object 的衍生。就算 Sun 把它們塞進了 Collection 泛型,仍然沒有讓那兩個類別之間出現什麼 interface。

前文中那三個類別,我希望它們的關係就像 Integer 與 String 的關係一樣乾淨。

實務上,我可能把那三個類別往3個不相干的地方擴展,而只有那一小部份內容偶然地出現重複的程式碼形式。我不希望為了那一小部份的偶然性,致使這3個本質上無關的類別拉上關係。

Java 也不是純OO語言,它沒有認為一切都是物件。原生型別不是物件、null不是物件、函數也不是物件。這點跟 C++ 一樣。但是 C++ 努力消弭原生型別與參考型別的差別。例如 " x + y " 這一段程式碼,在 C++ 中,我們不能區別哪一個是原生型別。但是在 Java 中,這兩者一定都不是參考型別,必定兩者都是原生型別,或兩者都是 String ,或 x 是 String。

還有Java早期, int i = 1; Integer j = i; 會失敗。當年學 Java1.0 時,碰到這事我整個人都傻住了。這跟 autoboxing 有關,想溫習一下古早味的,請在編譯時加選項 "javac -source 1.4 "

因為我用 C++ 時,習慣性的會寫 conversion constructor 。這種事我以為在 Java 中不必再做了。沒想到 Java 變本加厲,沒有 autoboxing 就算了,連 conversion constructor 也不給我們。直到 Java 1.5 才施捨 autoboxing 能力給我們。

一切都是 Java 自我限縮,不是我限縮 Java ,我只是說明而已。
如果 Java 有優點,我也會說明。奈何在我的對照組中,它的缺點比較多。

未留名 (#comment-20147911)
Sun, 06 Dec 2009 00:28:10 +0800
我就覺得int/Integer不一樣的感覺很好,我從JVM研究寫起的,而int/Integer本來就是不同的觀念的東西。我都用古早味寫程式,比如用了幾個framework都比我自己下sql不順手,又快又好維護!
除了jakarta commons我沒用任何已經寫好的framework(jakarta commons也不到framework等級吧),一切都自己來,我們的產品也佔領了台灣50%以上的市場,甚至外銷國外,都只用單純的java(J2SE/J2EE只用servlet/jdbc)!
如果只看古早味的java(泛指java 2.0),你會覺得當初的設計是有簡單的獨到之處,我也寫各種不同的程式語言,我認為一切不是Java自我限縮,是他引進了各種語言的優點,因為引進別的語言的優點而把那些簡單弄的複雜了。
inpines@gmail.com(同人) (#comment-20149791)
Sun, 06 Dec 2009 22:25:55 +0800
在我看來, Integer, String 是一種概念, 把它具體化賦予它意義的是人自己, 事實上, 概念是抽象的, 所以一個類別相依 Integer, String 甚至是 int, 與相依一個界面 whatever, 依務虛的角度沒有什麼不同. 只要這個界面是穩定的就夠了, 如果這個界面不穩定, 那代表這個界面所代表的概念還不夠完整.

我可以理解這是偏重實作的人不容易體會的, 我尊重他們的看法, 但我有一個習慣卻是不得不提醒他們, 世界上存在另一種不同觀點的設計理念.
未留名 (#comment-20150281)
Mon, 07 Dec 2009 01:36:47 +0800
像 M 兄的作法,大概也是另一種理念吧。

M 兄的作法,我在用 C、組合語言寫系統程式時也會這樣做。因為在那個層級中,我們必須明確地區別數值與參考/位址。

但是 Java 的自我定位似乎沒有那麼 low level。
inpines@gmail.com(同人) (#comment-20150823)
Mon, 07 Dec 2009 10:09:34 +0800
我想我要向 metavige 說聲抱歉,因為我好像誤解你最早對石頭成這個重構案例所要表達的意涵了。

仔細看看,解決石頭成的這個問題,在 c++ 我不會用 template,當然也不會在 Java 用 generic。因為這明顯是 template 或 strategy pattern 的問題,考慮 OCP,我會偏好用 strategy pattern 來解決這個問題。

至於 new 參數化型態的問題,如果把 strategy object 等同於石頭成的泛型物件來看,我實在不了解這樣做的目的。因為 strategy 通常會使用自委託來傳入實體,本身並不擁有任何實體,否則就失去虛的角色定位,而無法擴展它的復用性了。
未留名 (#comment-20154005)
Tue, 08 Dec 2009 02:09:47 +0800
我後來又想了想 M 兄的說法,突然聯想到 Bruce Tate 在《超越Java》書中的說法「當我們把 Java 延展到令人吃驚的不自然方向時,我們也會付出代價。」

就這一點來看,M 兄似乎也認為 Java 語言後來引進的新東西把 Java 弄得更複雜了。

to 同人:
你受 Java 編程影嚮太大,以至於你認為不用自委託就會失去虛的角色定位。

但是用 C++ template 可以直接這麼做,而且並不影嚮復用性。因為 template 的機制和 Java 的泛型並不相同。 template 會產生新的程式碼,所以 new DataType(); 最終會按參數值自動產生程式碼。而它自動產生的程式碼,處理的就是你們用 Java 時的自委託動作。

在 Java 的 strategy pattern 中,你在類別外用手寫下 new instance 的程式碼,然後呼叫委託方法將 instance 傳給 strategy 。
但是 C++ template 自動幫你產生 new instance 的程式碼,而且就是產生在 strategy 之內,所以委託動作也省了。

接著請把焦點放回到「需求」上,我的需求是「增加一個無參數的預設建構子」。這是因為我的 marshal 工具要這個東西,而遺憾的是 marshal 工具不懂什麼叫自委託,也不能讓我 autowired 一個實體進去。如果我什麼都不做,那麼 data 將會是未初始狀態,而 marshal 工具緊接而來的 getter 與 setter 動作將會發生例外。

結果是我必須寫新的程式碼才能解決這問題(我也可以不寫,繼續用原本可行但重複性很高的碼),而不是我原先預期的將幾個型別參數化即可的重
構。

說了那麼多東西,怎麼沒有任何人給我一個理由說明為什麼 Java 泛型不讓我們 new DataType(); 啊?難道 JVM 不能自己反射那個參數化型別後 new 出來嗎?

inpines@gmail.com(同人) (#comment-20155467)
Tue, 08 Dec 2009 15:35:10 +0800
石頭成,

你也受了批評 Java 編程的影響太大,你對我所下的假設全部不成立。你可能不知道,我是寫 c/c++ 出身的。而從你把泛型和多型最後會變在一起的說法,可見你還不懂得我說的自委託是什麼。我也沒說一定要自委託才能成為虛的角色,你是不是漏看了通常兩個字就急忙的對我下結論呀。

你的回應又讓我懷疑你好像也分不清楚組合和聚合的差別(自委託是聚合而不是你說的組合),至於你的疑問,既然知道用反射機制就可以解決你的問題,卻選擇鑽牛角尖而不解決問題,實在令人匪夷所思呀。

P.S. 你的需求不是需求,而是你假設可以解決需求的答案,或許你非用你的Marshal的工具,但如果是這樣,那就不是Java的問題,而是你工具的限制。

事實上,我過去用過其它Marshal的工具,連Hibernate產生出來的物件都可以沒問題的在網路上傳來傳去,所以我覺得你或許該想一想你的解決方法的前提是否有問題。
inpines@gmail.com(同人) (#comment-20155487)
Tue, 08 Dec 2009 15:41:14 +0800
但是用 C++ template 可以直接這麼做,而且並不影嚮復用性。因為 template 的機制和 Java 的泛型並不相同。 template 會產生新的程式碼,所以 new DataType(); 最終會按參數值自動產生程式碼。而它自動產生的程式碼,處理的就是你們用 Java 時的自委託動作。

我倒想看看 c++ 怎麼產出石頭成說的自委託程式碼,template 是二個不同的類別,何須用委託方式復用一段程式碼?

另外,自委託不是Java的實作方式,在GOF這本Design Pattern 有提自委託,但它可是用 c++ 展示這種手法的運用呀。
未留名 (#comment-20155527)
Tue, 08 Dec 2009 15:58:06 +0800
data = ((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
未留名 (#comment-20155541)
Tue, 08 Dec 2009 16:05:34 +0800
http://www.artima.com/weblogs/viewpost.jsp?thread=208860
Reflecting generics by Ian Robertson June 23, 2007

Type arguments to generic classes are not available for reflection at runtime - or are they? The type arguments for statically declared types can be discovered at runtime. A look at how to do this, and why you might want to.
未留名 (#comment-20157129)
Wed, 09 Dec 2009 02:09:11 +0800
呵,我不是資訊科班生,自學起家的人,也許用字沒那麼專業。不過我是看著 Martin 的書上說的 strategy 例子寫回應。

我沒說 C++ 產生了自委託程式碼。一切如你所知, C++ 產生了兩個新類別的程式碼(或許我該說它產生了 Template method的程式碼),而它產生的程式碼處理的是我們用 Strategy 想做的事。
Ok, 如此一來有2個方式可以解決我的需求。但是在 C++ 中,一個方式是編譯器幫我產生,另一個要我自己寫程式碼。我比較懶,在可以選擇的時候,我需要編譯器幫我做。
回到 Java ,兩個方式都需要我自己寫。我的選擇是... 程式碼很多的才重構;程式碼少的,我就放著不動。重構也要成本,而我算了一算,Java 語言的重構成本挺高的,很多地方不如不管。

編輯一句話,修改為「在 Java 實現 strategy pattern 時,你在類別外用手寫下 new instance 的程式碼...」。如果有人因為這句話而誤解某種 Design pattern 只能用特定語言去寫的話,我在此說聲抱歉。

既然知道用反射機制就可以解決你的問題,卻選擇鑽牛角尖而不解決問題,實在令人匪夷所思呀。
理由很簡單啊,因為我反射不出來嘛。後來我知道泛型資訊在執行時期不存在,所以 JVM 不能幫我反射。

我最後確實是用別的方法解決了問題,但是泛型在這方面施展不開也是事實,我需要留個記錄提醒自己:Java的泛型不是讓我做這件事用的。

謝謝 arthuroy 的解答。這個答案真是... 嗯,令人仰之彌高,望之卻步。
未留名 (#comment-20160749)
Wed, 09 Dec 2009 09:25:59 +0800
to 同人:

我現在倒是忘了,我當初為什麼要那樣說了~~
只是個很直覺的感覺,大概想說石頭成的說法,我看起來有點怪怪的

至於java的泛型,我現在想,的確不是能夠像c++一樣,用來作template, 所以,拿這兩樣來相比,就有點對不起來了

我現在想,泛型在java的意義,比較像是為了在編譯期間,避免有多餘的轉型程式以及強型別的定義所用的

所以,或許 java 真的就是沒有 template 這件事情,如果要達到這樣的『效果』,就得用 Strategy DP 來達到了吧~~
inpines@gmail.com(同人) (#comment-20160807)
Wed, 09 Dec 2009 10:02:27 +0800
石頭成,

我也不是科班出身的,唸了 EMBA 資管系也是在我有20年軟體開發的經驗之後,所以對軟體設計的理論與實務,也是得自於興趣與旺盛的學習心吧,我想這大家都一樣吧。

或許我該說它產生了 Template method的程式碼

我還是不得不說,這個推論恐怕是錯的,c++不會把重覆程式碼抽出來,而是運用具現化的方式產生 n 個新類別,所以這樣根本不會產生 template method 的程式碼。我覺得這句話更大的問題是把多型和泛型當成同樣的東西,那是很大的迷思。多型與泛型是解決重覆程式碼不一樣的兩條路,出發點雖然可能相同,但終點經常不會走在一起。

我懷疑你的案例有更簡潔的方式,但待我測試沒問題再提吧!
inpines@gmail.com(同人) (#comment-20160863)
Wed, 09 Dec 2009 10:35:58 +0800
在 Java 實現 strategy pattern 時,你在類別外用手寫下 new instance 的程式碼...

運用state/stragegy等DP,生成組合物件,本來就是運用這些DP所需要解決的問題。你可以用生成樣式,或寫在主程式碼的初始化,不管用什麼語言實作都一樣。另外,被組合的hiding object(指state或strategy)通常會聚合組合它們的物件,以取得執行演算法需要的資料,這也是不論用什麼語言實作都一樣。

我倒建議,石頭成多動手做幾次這種DP,或許會比較清楚其精神,看書意會,總是會有些誤解的風險。
inpines@gmail.com(同人) (#comment-20160909)
Wed, 09 Dec 2009 10:48:42 +0800
Hi metavita,

泛型在java的意義,比較像是為了在編譯期間,避免有多餘的轉型程式以及強型別的定義所用的

所以,或許 java 真的就是沒有 template 這件事情,如果要達到這樣的『效果』,就得用 Strategy DP 來達到了吧~~

的確這是 Java 泛型的一個常見功能,但如果這是主要的功能,那這種改變未免價值太低了。以前用強制轉型的程式用得好好的,非得要改成型態安全的碼,實質意義大嗎?

以我本身的經驗,泛型最大的好處大概是Generic DAO,如同我前面回應留下的文章連結,它是在多型之餘,再多了一層型態參數化,使得演算法和資料型態可以分開。如果以前多型可以做得到,沒有必要等泛型出現才進行實作,而要寫讓人不滿意勉強可以接受的重覆程式碼。

由此可見,多型和泛型是用來解決不同的問題。不該混為一談,至於template,是用來進行泛型程式設計的一種實作方式。
inpines@gmail.com(同人) (#comment-20161201)
Wed, 09 Dec 2009 12:07:34 +0800
Hi 石頭成,

研究了一下你的案例,確實我找不到更簡潔的方式處理無參數的預設建構子。這大概是很多實作多把物件類別當參數傳入建構子的原因吧。Authuroy 提供的方法的確會令人望之卻步,但如果是我,我會運用設計來解決問題而非實作。

我知道你好像最近在寫web service的ap,其實早在二三年前,我用jsf+spring+hibernate+xstream就已經實作出SOA的架構了,當年沒有用generic,而今天重構成generic的版本,絲毫也沒有遇到你說的問題,而且大量運用反射機制與proxy物件。整個系統只用一支web service就夠了,開發人員甚至不需要知道有web service的存在,或許這就是設計跨越實作的隔閡的明證吧。
inpines@gmail.com(同人) (#comment-20162349)
Wed, 09 Dec 2009 17:59:41 +0800
在 Java 的 strategy pattern 中,你在類別外用手寫下 new instance 的程式碼,然後呼叫委託方法將 instance 傳給 strategy 。
但是 C++ template 自動幫你產生 new instance 的程式碼,而且就是產生在 strategy 之內,所以委託動作也省了。

原諒我很雞婆地再多嘴一下,因為石頭成上面這段話並不精確。

實作strategy pattern通常會把strategy object變成hiding object,也就是外界並不知這個策略物件的存在,是由組合此策略的物件在生成時偷偷產生的,通常會搭配Abstract Factory依據組態檔生成。當然策略物件會需要主物件傳入參考給它,好讓它得到資料,這也代表策略物件並不保存任何的狀態。

C++ 的 template 並不會產生以上的程式碼,只會依據不同的資料型態在編譯或連結時期具現不同的類別。編譯出來的程式碼就好像你重覆依據不同的資料型態對同一種演算邏輯寫不同的類別一樣,只是template可以防止因為error wording產生錯誤而已。

所以泛型不能與多型相提並論,前者處理的是泛型concept,並且符合某種constraint,而後者則是抽象類別或界面,兩者雖然有點像,但它們是完全不一樣的東西,而且不能互通,這是我一直想強調的觀念。
未留名 (#comment-20163241)
Thu, 10 Dec 2009 00:14:50 +0800
繞了一圈,又回到原點啦。

同人:C++ 的 template 並不會產生以上的程式碼,只會依據不同的資料型態在編譯或連結時期具現不同的類別。
我補充一下,他是說 C++ template 不會產生 strategy pattern 需要的程式碼。

至於 C++ template 的做法,其實我在 前文 從 C++ Template 到 Java Generic,一步一步來 就已經用 PHP 模擬了它的動作,連 C++ compiler 實際上會產生一個新的類別簽名都提了。

Martin 的書上是用 Java ,並將 Template 與 Strategy pattern 放在一起比較說明。為了讓未接觸過 C++ 的人更便於理解,所以我回應才附注說可以從 Java 的 Template pattern 去想像 C++ template 大概是在做什麼。但是實際上跟 template pattern 仍然不同。

在前文的例子中,C++ template 實際上會產生三個沒有關係的新類別,不牽涉多型;還是不懂的讀者,請你自己打開編輯器,把類別定義的碼複製下來,貼上,然後用搜尋取代的功能換掉型別的部份,重複三次。這就是 C++ compiler 幫你做的事。

而 template pattern 則用了繼承,所以牽涉到多型。雖然名字很像,但不能相提並論。

同人:如果是我,我會運用設計來解決問題而非實作。我知道你好像最近在寫web service的ap,其實早在二三年前,我用jsf+spring+hibernate+xstream就已經實作出SOA的架構了

唉,這就是問題了。我只是個小小的programmer,在這件案子中只負責一小部份,並不是架構設計師。牽扯到設計變更的部份,並不是我能動手的。所以我只能透過實作手段來重構。

再者,這件案子到目前為止才跑一個月多些(我隔了十年再次接觸Java的時間才一個月多...),時程趕在年底預算結算前。雖然在這之前已經有部份的架構設計,但並不像同人那種維護了兩三年之久的設計。而且 Java 程式碰到要變更設計時的邊際效應很高,在趕專案的情形下,更難去變更已經在跑的設計。

在這種侷限下, Java 語言能提供的助力如此之小,更令我覺得失望。
inpines@gmail.com(同人) (#comment-20163999)
Thu, 10 Dec 2009 10:13:42 +0800
補充一下,我在那個專案是架構者,但卻不是只設計不實作的架構者。那個專案採用TDD+Refactoring的XP實務,在1/9新竹場的敏捷開發分享會我會提我的經驗(不好意思廣告一下),而我當時的改善設計的策略是很多事我只會做,不會說,除非我已經有成果可以展現,以及Rome was not build in a day:)
inpines@gmail.com(同人) (#comment-20164021)
Thu, 10 Dec 2009 10:20:09 +0800
Rome was not built in a day,我相信變更設計的邊際效應不在於工具,因為那樣的想法不是事實,而是我們對工具的主觀價值判斷所得到的信念。它創造了設計無法更好的實相,而這種實相是可以改變的,軟體開發最有趣的是創造力可以改變很多事。
未留名 (#comment-20164317)
Thu, 10 Dec 2009 11:53:35 +0800
我真的搞不懂這個議題。當年的舊的java有支援generic嗎?就是一堆人覺得某個語言有個功能很好,所以要java支援,接著一個白痴做了這個一半的功能來讓人罵?
比如說Erlang今天跟java一樣多人用,大家都說x語言的功能很好,沒這功能就是爛語言,又一個白痴做了上去後又被罵做一半,根本沒有w,x,y,z語言的好用?
不能重寫compiler嗎,scala的例子很成功啊!或是輔助性的語言如aspectJ,再不然用script如jython這樣,讓該煩惱的人去做compiler,而不是把所有東西都放到java,事實上這樣做的專案非常多也都很成功。
學別的語言很難嗎?你的根基還是用java啊!我覺得學什麼spring這些framework更難,尤其xml超級難debug的,甚至把程式寫在xml裡面,簡直是災難!就說那些語言是framework就好了啊!
未留名 (#comment-22482946)
Mon, 11 Jun 2012 21:09:10 +0800
T GetObj() where T:new()
{
return new T();
}
未留名 (#comment-22482950)
Mon, 11 Jun 2012 21:11:06 +0800
更正:
T GetObj<T>() where T:new()
{
return new T();
}

..石頭大你的網誌打不出半型的<>
yuri@hotmail.com(tiara) (#comment-22615646)
Thu, 13 Sep 2012 14:33:36 +0800
動態多型和靜態多型各有適合使用的時機,彼此可說是兩條平行線
不過在java和C++中,我們經常都會同時使用這兩者,互相輔助
也許因為這樣,導致很多人搞不清楚兩者的區別和使用的時機
老問題,台灣資訊業太不注重基礎功