2006年5月22日
應(yīng)朋友邀請(qǐng),周六早從上海出發(fā)往杭州參加阿里巴巴網(wǎng)俠大會(huì)。同行四人,有銳道的macro chen、楊光(還是我?guī)煹埽⒁苿?dòng)的王偉旭(特長(zhǎng)是linux和網(wǎng)絡(luò)安全,也是中國(guó)linux推廣的先驅(qū))。一路上,言談甚歡。老莊給我們訂的票,他一早腸胃有恙,仍然堅(jiān)持把票送到火車(chē)站,之后去吊鹽水,下午又出現(xiàn)在會(huì)場(chǎng)。確實(shí)精神可嘉,建議阿里巴巴頒發(fā)“最佳精神獎(jiǎng)”。
到杭州已是中午,錯(cuò)過(guò)了上午大會(huì)。下午Robbin進(jìn)行Java技術(shù)展望和RoR實(shí)現(xiàn)REST的演講,既然是朋友,肯定是要捧場(chǎng)的。Robbin旁征博引,以其深厚的技術(shù)功底和對(duì)新技術(shù)的敏銳洞察贏得了聽(tīng)眾。
晚上一堆人去聚會(huì),各路豪杰紛至:有阿里巴巴的,有自己創(chuàng)業(yè)的,有技術(shù)大牛,還有媒體(Infoq),出版社(博文的周總領(lǐng)3員大將赴會(huì))。大家互換名片,認(rèn)識(shí)的不免寒暄幾句,不認(rèn)識(shí)的也很快就熟捻了,還不時(shí)有“原來(lái)你就是×××”的驚呼,原來(lái)網(wǎng)上就“互通心曲”,只是一直沒(méi)機(jī)會(huì)認(rèn)識(shí)罷了。
席間觥籌交錯(cuò),具體內(nèi)容暫且不表,只說(shuō)一件令我感受頗深之事。一個(gè)阿里巴巴的員工表現(xiàn)出對(duì)公司的無(wú)比忠誠(chéng),講起公司的獎(jiǎng)懲制度,說(shuō)是一個(gè)員工的績(jī)效不僅跟所在項(xiàng)目相關(guān),還與部門(mén)、其它部門(mén)甚至整個(gè)公司的業(yè)績(jī)相關(guān)。所以只要是對(duì)公司有利的事情,即使與自己現(xiàn)在的工作無(wú)關(guān),他們也會(huì)去做。按常理來(lái)說(shuō),這有點(diǎn)不公平,我只能努力做好自己的事情,而如果別人不努力,我就是白做。但如果大家都努力,又變成了共贏。
這里讓我講一個(gè)簡(jiǎn)單的博弈問(wèn)題,就是“囚徒困境”。A和B兩個(gè)同犯被抓,因?yàn)闆](méi)有其它任何證據(jù)和證人,只能讓2人分別交供。如果A和B都矢口否認(rèn),那么兩人無(wú)罪釋放。如果A承認(rèn),B不承認(rèn);A是坦白從寬,判1年;B抗拒從嚴(yán),判5年,反之亦然。如果2人都承認(rèn),ok證據(jù)確鑿,各判2年。如果2人都是理性人,且沒(méi)有互通消息,按照博弈,每個(gè)人的最優(yōu)解就是承認(rèn),也就是各判2年。其實(shí)對(duì)2人真正有利的就是打死不承認(rèn)然后都無(wú)罪釋放,而這種狀態(tài)在理性人的假設(shè)下是很難實(shí)現(xiàn)的--除非有一個(gè)教父,一直灌輸他們不要出賣(mài)同伙。
馬云就是這個(gè)“教父”!
卡內(nèi)基有篇文章,我總結(jié)成一句話就是:用崇高的理想打動(dòng)別人。據(jù)說(shuō)馬云一直是以個(gè)人魅力及“創(chuàng)造中國(guó)電子商務(wù)的明天”類似的理想,激勵(lì)員工的。有了統(tǒng)一的企業(yè)文化,員工都不計(jì)較個(gè)人得失,努力奮進(jìn),最終企業(yè)和所有員工取得共贏,這絕對(duì)是擺脫“囚徒困境”的典型案例。
話說(shuō)回來(lái),阿里巴巴能讓你感受到團(tuán)隊(duì)的力量,一群精英在一塊做很有價(jià)值的事情,對(duì)每個(gè)人也是很好的鍛煉。個(gè)人認(rèn)為,如果有吃苦耐勞的打算,眼光放長(zhǎng)遠(yuǎn)點(diǎn),又沒(méi)有其它方面的束縛,阿里巴巴的確是不錯(cuò)的選擇。(得向阿里巴巴收代言費(fèi),呵呵!)
第二天聽(tīng)了多場(chǎng)論道,主要是SAAS,搜索,分詞方面。結(jié)合阿里巴巴的戰(zhàn)略,我把幾點(diǎn)融合起來(lái)講一下。這個(gè)下篇再細(xì)細(xì)道來(lái)。
這次給
openfans
做網(wǎng)摘功能,主體程序倒是很快就寫(xiě)完了,另外要做個(gè)
IE
插件,卻碰到了不少問(wèn)題。
IE
插件其實(shí)很簡(jiǎn)單,就是用
js
獲得頁(yè)面的標(biāo)題、
url
和選擇的內(nèi)容,然后通過(guò)彈出窗口,將其送到服務(wù)器。這里就有中文的問(wèn)題了,開(kāi)始使用
escape
,如
escape(title)
形式,
request.getParameter
碰到中文就為
null
,網(wǎng)上搜了一通,說(shuō)是可以通過(guò)
java
編碼搞定,但拿到就為
null
了,還怎么換編碼?忙活了好幾個(gè)小時(shí),又是
alert
,又是
document.write
,看上去也沒(méi)什么問(wèn)題。不
escape
,直接在瀏覽器中輸入帶中文的
url
,拿到的不為
null
了,拿到后,通過(guò)
new String(str.getBytes("ISO-8859-1"), "UTF-8");
還真顯示正常了。但用
window.open
又出亂碼了。看到文章說(shuō)還有
encodeURIComponent
方法可用,就試了下,把
escape
換成
encodeURIComponent
居然搞定了,服務(wù)端還是得用
new String(str.getBytes("ISO-8859-1"), "UTF-8")
進(jìn)行處理。注意這里用的
tomcat
,它的默認(rèn)編碼就是
"ISO-8859-1"
,如果改了編碼程序也得做相應(yīng)的改動(dòng)了。
今天為了在本機(jī)裝個(gè)wordpress玩玩,搞了搞php5+mysql5+apache2。網(wǎng)上搜了一篇文檔,很快就讓php與apache跑起來(lái)了,但連mysql始終不行。報(bào)錯(cuò):Call to undefined function mysql_connect()。查了一下半天,就是php關(guān)于mysql的ext沒(méi)配好,但我改了php.ini啊,也把"extension=php_mysql.dll"放出來(lái)了。查了好久,看到一篇說(shuō)php5需要加上"extension=php_mysqli.dll",試了下果然好了。
???? 然后需要以index.php作為默認(rèn)的welcomefile(不知道怎么叫,web.xml里是這個(gè)),需要在"DirectoryIndex index.html index.html.var"后加上 index.php就行。
然后飛快的裝了phpmyadmin、dvbbs的php版。發(fā)現(xiàn)php應(yīng)用的安裝的確很是方便,解壓,拷貝到htdocs下,馬上就能運(yùn)行了,比java應(yīng)用簡(jiǎn)單的多,更別提復(fù)雜的要死的企業(yè)應(yīng)用了。這點(diǎn)上java要好好向php學(xué)習(xí)啊。
項(xiàng)目需要,開(kāi)始研究電子支付。國(guó)外的電子支付提供商,得好好研究它的文檔和api。全是e文,只能慢慢看了。
? 學(xué)習(xí)了下spring2.0。對(duì)openfans而言,有2個(gè)比較重要的改進(jìn)。首先是aspectj的支持,可以方便的使用aspectj語(yǔ)法定義aspect和pointcut了,openfans準(zhǔn)備在domain object的自動(dòng)注入上和權(quán)限等方面使用aop。另外就是spring form標(biāo)簽庫(kù)的引入,現(xiàn)在springmvc也有自己的標(biāo)簽庫(kù),以前自己給checkbox和radio寫(xiě)的request.getParameter可以改寫(xiě)了。
摘要: 應(yīng)項(xiàng)目需要做了一個(gè)定時(shí)更新的
cache
框架,采用
spring+quartz
很方便的實(shí)現(xiàn),可以適用任何需要定時(shí)才更新的地方,比如靜態(tài)網(wǎng)頁(yè)
cache
等。代碼很簡(jiǎn)單:
---------------------------------QuartzCacheHandler-------------------...
閱讀全文
接著前面的寫(xiě)。上文主要寫(xiě)了
ajax
在
portal
中的使用,這篇寫(xiě)集群方面的體會(huì)。現(xiàn)在比較流行的架構(gòu)就是前端
F5
做負(fù)載均衡,后面
2
臺(tái)
websphere server
做成集群,各自都有
HttpServer
,每個(gè)
HttpServer
都向
2
臺(tái)
was
做轉(zhuǎn)發(fā)。這樣每臺(tái)都能獨(dú)立完成從
HttpServer
到
was
的流程。一臺(tái)出現(xiàn)故障,
F5
首先進(jìn)行切換,只向正常
server
的
HttpServer
發(fā)起請(qǐng)求,這臺(tái)
HttpServer
再進(jìn)行切換只向同一臺(tái)
server
上的
was
做轉(zhuǎn)發(fā)。這次
portal
就是采用的這種架構(gòu),不妨稱為架構(gòu)
A
。
另一種簡(jiǎn)單點(diǎn)的架構(gòu)就是只做
F5
負(fù)載均衡,不做
was
集群,每臺(tái)
websphere server
上的
HttpServer
接受
F5
轉(zhuǎn)發(fā)的請(qǐng)求,只向本
server
的
was
轉(zhuǎn)發(fā)。這樣每臺(tái)
websphere server
保持獨(dú)立,相互間沒(méi)有數(shù)據(jù)交換和轉(zhuǎn)發(fā)。不妨稱為架構(gòu)
B
。
架構(gòu)
A
和
B
各有優(yōu)劣,適合不同的需要,下面進(jìn)行些比較:
?????????
從應(yīng)用部署上看:
A
使用了
websphere
集群,由一個(gè)
DeployManager
進(jìn)行分發(fā),部署應(yīng)用,只需部署一次,由
DM
分發(fā)到幾個(gè)節(jié)點(diǎn)上。而
B
每個(gè)
server
都是獨(dú)立的,部署應(yīng)用只能一臺(tái)臺(tái)部署,如果
server
較少差別還不明顯,如果達(dá)到
10
臺(tái)以上,一臺(tái)臺(tái)部署將是一個(gè)比較痛苦的事情。
?????????
從
session
上看:
A
使用了
websphere
集群,可以使用集群提供的
session
復(fù)制,對(duì)于一些關(guān)鍵應(yīng)用(某臺(tái)服務(wù)器宕機(jī),
session
也必須保持的應(yīng)用)很有必要。而對(duì)于一些能夠允許
session
丟失的應(yīng)用,才可以使用
B
。當(dāng)然
A
也可以關(guān)閉
session
復(fù)制,因?yàn)?/span>
session
復(fù)制不管是使用數(shù)據(jù)庫(kù)方式還是內(nèi)存方式,總會(huì)消耗一定的性能。具體消耗多少性能,就要看不同的
application server
的
session
復(fù)制方案了,想深入了解,可以看集群方面的文檔,我也只記得一個(gè)比較簡(jiǎn)單的
round robbin
了。
?????????
從架構(gòu)復(fù)雜性看:
B
更為簡(jiǎn)單,因?yàn)闆](méi)有
DM
的概念,每臺(tái)
server
都保持獨(dú)立。而使用了
DM
有時(shí)也會(huì)出現(xiàn)莫名奇妙的問(wèn)題,這當(dāng)然是由于不了解
DM
的機(jī)制所致,但總歸也增加了復(fù)雜度,這點(diǎn)在后面的教訓(xùn)中進(jìn)行說(shuō)明。
?????????
從水平擴(kuò)展性上看:
B
肯定更勝一籌。只要
F5
能支持,多少臺(tái)
server
都沒(méi)關(guān)系。而
A
多臺(tái)
server
做集群,要看
websphere
支持的節(jié)點(diǎn)數(shù)量,應(yīng)該不會(huì)太大。這個(gè)如果哪位同學(xué)知道,敬請(qǐng)告知。
當(dāng)然
A
和
B
在服務(wù)器較多的情況下是可以共存的,可以考慮幾臺(tái)機(jī)器做集群,然后集群間做負(fù)載均衡,這樣既可以減少部署的復(fù)雜度,又可以帶來(lái)較好的水平擴(kuò)展。由于沒(méi)做過(guò)更大型的項(xiàng)目,這個(gè)也只是我的假象,請(qǐng)做過(guò)的同學(xué)斧正。
?
說(shuō)一說(shuō)集群中碰到的問(wèn)題。
?????????
首先是對(duì)各節(jié)點(diǎn)的同步:
有時(shí)為了方便測(cè)試,我們只對(duì)其中一個(gè)節(jié)點(diǎn)進(jìn)行更改,測(cè)試通過(guò)再放到其它節(jié)點(diǎn)。而如果測(cè)試周期較長(zhǎng),有時(shí)就會(huì)造成節(jié)點(diǎn)的不同步,出現(xiàn)各種各樣莫名其妙的問(wèn)題。一個(gè)經(jīng)驗(yàn)就是:無(wú)論如何,在每天下班前要保證各節(jié)點(diǎn)的同步,不同步的現(xiàn)象不要過(guò)夜。
?????????
然后是對(duì)
DM
的理解:
我現(xiàn)在還只是實(shí)踐階段,沒(méi)有看過(guò)相關(guān)文檔。從意義上看,它控制了相關(guān)的配置文件,如果進(jìn)行節(jié)點(diǎn)同步,就會(huì)由它把配置文件同步到它管理的節(jié)點(diǎn)上。這對(duì)配置文件的修改提出了要求。我們開(kāi)始只修改節(jié)點(diǎn)的配置文件而沒(méi)有修改
DM
的,結(jié)果進(jìn)行節(jié)點(diǎn)同步就會(huì)覆蓋修改的配置文件,帶來(lái)很多不必要的工作。經(jīng)驗(yàn)就是:或者修改
DM
的配置文件,然后進(jìn)行節(jié)點(diǎn)同步,或者直接同時(shí)修改所有節(jié)點(diǎn)和
DM
的。
?????????
還有關(guān)于
cache
的:
Cache
是性能優(yōu)化的一個(gè)有效手段。在單機(jī)環(huán)境下,最簡(jiǎn)單的就是內(nèi)存
cache
,使用
static
的
Map
就行。而在集群環(huán)境中,
cache
就變的比較復(fù)雜了。首先還是從應(yīng)用需求入手,是否要保持每臺(tái)機(jī)器的
cache
同步。如果只是信息展示等要求不高的
cache
,不需保證
cache
的同步,問(wèn)題也比較簡(jiǎn)單,自己寫(xiě)內(nèi)存
cache
,或者使用開(kāi)源的
cache
組件如
ehcache,oscache
等就可以很好的解決問(wèn)題。而如果需要
cache
在幾個(gè)節(jié)點(diǎn)保持同步,就需要特殊的機(jī)制了,
ehcache
等號(hào)稱支持分布式
cache
,但好像需要
jgroup
,配置比較麻煩,我沒(méi)有用過(guò),有用過(guò)的同學(xué)請(qǐng)指教。我本來(lái)想使用
session
保存,然后進(jìn)行
session
同步,后來(lái)
IBM
建議使用數(shù)據(jù)庫(kù)
cache
,即自己寫(xiě)代碼,
cache
在數(shù)據(jù)庫(kù)中。這樣不需要
session
同步,對(duì)象不大,性能也能得到保證,現(xiàn)在用下來(lái)效果還可以。
?
這次做
ibm
的
portal
,算是臨危受命。做了幾個(gè)月的
SA
離職,留下一個(gè)功能和性能都有很多問(wèn)題的項(xiàng)目,臨時(shí)讓我頂上。經(jīng)過(guò)一個(gè)多月的緊張工作(經(jīng)常加班,上班上不了網(wǎng),也沒(méi)時(shí)間上網(wǎng)),總算功能和性能上都能達(dá)到客戶要求了。而我也由一個(gè)不懂
portal
的人,經(jīng)過(guò)項(xiàng)目中實(shí)戰(zhàn),不說(shuō)成為高手,一般的概念、開(kāi)發(fā)、配置、優(yōu)化等也都有了很多體會(huì)。
這次技術(shù)上值得推薦的就是合理的使用
ajax
,既加快了首頁(yè)的
load
速度,又帶來(lái)了很好的用戶體驗(yàn)。開(kāi)始首頁(yè)上所有
portlet
都是串行加載,有的
portlet
比如新郵件,依賴于
mail
系統(tǒng)提供的接口。開(kāi)始這個(gè)接口在較大壓力下就出現(xiàn)性能瓶頸,后在我們的要求下替換了協(xié)議,性能也在
1s-2s
之間。如果采用常規(guī)的辦法,加上
wps
驗(yàn)證、運(yùn)算,顯示主題、皮膚,加載所有
portlet
,響應(yīng)時(shí)間肯定在
10s
以上。
我在
openfans
中使用了
ajax
,有些經(jīng)驗(yàn),所以決定采用異步加載:首頁(yè)
load
時(shí)一些
portlet
直接顯示正在
loading
的字樣,在
body onload
時(shí)再使用
ajax
填充內(nèi)容;使用
iframe
的
portlet
,也是
src
先指向一個(gè)靜態(tài)的正在
loading
頁(yè)面,
body onload
時(shí)再替換
src
到實(shí)際地址(這是
ajax
模式的一種)。這樣首頁(yè)登錄實(shí)際上只經(jīng)過(guò)
wps
內(nèi)部的驗(yàn)證和顯示,所有業(yè)務(wù)邏輯都是加載成功后再并行進(jìn)行。實(shí)際表現(xiàn)效果就是:頭上的主題很快出來(lái),一塊塊區(qū)域顯示正在
loading
字樣,性能快的
portlet
很快出來(lái),需要幾秒的
portlet
隨后出來(lái),而不是讓用戶傻等
10
多
s
再一下全部顯示。
使用
ajax
同時(shí)也能解決頁(yè)面刷新問(wèn)題和獲取返回值的問(wèn)題。比如前面顯示新郵件的
portlet
,用戶點(diǎn)擊了一封郵件,新郵件數(shù)應(yīng)該減
1
,剛點(diǎn)擊的郵件也應(yīng)該上頁(yè)面上消失。原始的做法就是刷新整個(gè)頁(yè)面,既加大服務(wù)器壓力,又帶來(lái)很差的用戶體驗(yàn)。使用
ajax
,在點(diǎn)擊后
1s
(或者更長(zhǎng),這取決于郵件系統(tǒng)對(duì)點(diǎn)擊操作的響應(yīng)快慢)刷新
div
的內(nèi)容,用戶甚至感覺(jué)不到內(nèi)容已經(jīng)更新。其它
portlet
也不需要重新載入,大大減輕服務(wù)器的壓力。有的操作需要提交給其它系統(tǒng),而且可能成功可能失敗,這就需要獲得返回值。如果使用普通的
form
提交,需要更新整個(gè)頁(yè)面。而使用
ajax
提交,可以方便的獲得其返回值,進(jìn)而顯示不同的提示。
另一個(gè)架構(gòu)上的特點(diǎn)就是
portal
服務(wù)器職責(zé)單一
。開(kāi)始所有的業(yè)務(wù)邏輯都是寫(xiě)在
portlet
里,加重了
portlet
服務(wù)器的壓力。我進(jìn)來(lái)后做的一個(gè)大的規(guī)劃就是,把業(yè)務(wù)邏輯抽離到其它
server
上,然后通過(guò)
ajax
加載到
portlet
中。這樣既可以充分利用服務(wù)器資源(新的
server
使用單獨(dú)的內(nèi)存空間和線程池),又使得
portal
服務(wù)器職責(zé)更單一:僅進(jìn)行驗(yàn)證、權(quán)限控制、主題、皮膚和
portlet
的展示。
先寫(xiě)這么多。因?yàn)槭褂昧?/span>
2
臺(tái)
server
做集群,在分布式環(huán)境下,開(kāi)發(fā)也有了更多的要求(比如
cache
),后一篇文章再細(xì)細(xì)道來(lái)。
難得有空,寫(xiě)篇程序之外的文章,關(guān)于壓力的,也是自己近來(lái)的親身體會(huì)。
眾所周知軟件這行壓力是很大的。各種各樣的問(wèn)題層出不窮,每天上班工作內(nèi)容都是排的滿滿的,遇到突發(fā)問(wèn)題就得加班。如果不及時(shí)進(jìn)行疏解,積累到一定的程度,就可能產(chǎn)生一定的負(fù)面問(wèn)題,比如上班精神狀態(tài)差、注意力不能集中、遇事喜歡逃避等等。我就親身經(jīng)歷了這樣的狀況,明知自己工作積極性差、效率很低,但也很難一下子找回自我。
一次偶然的出游讓我從中很快走了出來(lái)。一個(gè)親戚考上廈大的博士,我請(qǐng)了
2
天的假,利用周末時(shí)間順便去廈門(mén)旅游。廈門(mén)依山傍海,的確是旅游的好去處。晚上到海邊,涼風(fēng)習(xí)習(xí),光腳沿著沙灘走過(guò),任起落的潮水在腿上腳上留下層層薄沙。內(nèi)心也變得平靜,能夠感受到海的呼吸。天地間仿佛只剩下我和大海,在進(jìn)行心靈的交流,俗世煩擾皆拋諸腦后,只剩下對(duì)海的依戀。白天去爬南普陀山,并不太高,慢慢爬到山頂,整個(gè)思明區(qū)盡收眼底,遠(yuǎn)處一艘快艇在海面掠過(guò),留下一條美麗的浪花。然后順山而下到植物園,途徑無(wú)數(shù)奇花異草、層天老樹(shù),走得累了,找個(gè)湖邊石凳休息一下,人也覺(jué)得輕松愉快。
經(jīng)過(guò)大自然的洗禮,回到單位,人的精神面貌煥然一新,抱著積極的心態(tài)處理事情,很多問(wèn)題迎刃而解。壓力測(cè)試做的很累,經(jīng)常要熬夜,但通過(guò)一輪輪的測(cè)試,逐步定位到性能問(wèn)題所在,自己也學(xué)了不少相關(guān)知識(shí),想想也就沒(méi)那么煩了。
做事的方式,也有了長(zhǎng)進(jìn)。我現(xiàn)在信奉人一時(shí)只做一件事效率最高的原則。事情再多,也是一件件做,每天安排好近日的工作,并排個(gè)優(yōu)先級(jí),什么是要親自處理的,什么是讓別人處理的,什么是需要預(yù)先通知他人的,需要什么資源,每件事情的預(yù)計(jì)時(shí)間如何,需要如何
check
等等。做好一件事就打個(gè)勾,做到心中有數(shù)。如果事情有延誤,分析是什么原因,該如何補(bǔ)救,而不要有太大的心理負(fù)擔(dān),自己盡力了就好,是自己的責(zé)任就要勇敢扛下,死不了人的。這其實(shí)是很簡(jiǎn)單的原則,誰(shuí)都能夠?qū)W會(huì),但的確很管用。
總結(jié):壓力是無(wú)處不在的,關(guān)鍵在于如何應(yīng)對(duì)和排解。用積極的心態(tài)和恰當(dāng)?shù)姆椒鎸?duì),壓力也就沒(méi)那么大了。感覺(jué)壓力積累到一定程度,在還未影響正常工作之前就先想辦法排解,出去旅游、運(yùn)動(dòng)等都是緩解壓力的好辦法。
最近對(duì)項(xiàng)目組的一些較差的代碼進(jìn)行了些重構(gòu),同時(shí)靈光一閃,對(duì)代碼有些比較形象的比喻。
壞的代碼就象揉面團(tuán),管什么接口什么實(shí)現(xiàn)全揉成一團(tuán),一個(gè)方法幾百行,注釋寫(xiě)再多也是面團(tuán)(夾了些小紙條而已)。然后需要重用了,就是從中抓起一把面團(tuán),然后放到其它的面團(tuán)里繼續(xù)揉。這樣重復(fù)代碼一堆,什么易讀性、擴(kuò)展性、可維護(hù)性都是無(wú)從談起。
好的代碼就象堆積木,接口實(shí)現(xiàn)定義清清楚楚,每個(gè)接口只做一件事情,重復(fù)代碼都是通過(guò)更細(xì)的接口來(lái)消除。重用就是把積木塊往該放的地方堆,這樣的代碼,幾個(gè)大塊幾個(gè)小塊一目了然,只要方法命名規(guī)范,連注釋都可以省去。這樣耦合性低,易讀性、擴(kuò)展性、可維護(hù)性都可以得到保證。
把面團(tuán)變成積木并不復(fù)雜,定義好模具,面團(tuán)一團(tuán)團(tuán)往里面填充,待穩(wěn)定下來(lái),就成了一塊塊積木。這里關(guān)鍵就是模具的制作,推薦制作寶典:
martin fowler
的那本重構(gòu)。還得有模具的丈量工具,就非
junit
莫屬了。
??? 項(xiàng)目需要寫(xiě)了幾個(gè)數(shù)據(jù)庫(kù)同步用的
trigger
,就是記錄用戶的操作到一個(gè)
temp
表,然后每天通過(guò)
webservice
同步到其它系統(tǒng),同步成功清空該
temp
表。自認(rèn)為寫(xiě)的還行,做個(gè)記錄。是
db2
的。
?
--
用戶組新增觸發(fā)器
--DROP TRIGGER TG_USERG;
CREATE TRIGGER LIBING.TG_USERG AFTER INSERT ON LIBING.TM_USERG
? REFERENCING NEW AS NROW
? FOR EACH ROW
? MODE DB2SQL??
? BEGIN ATOMIC
?
? declare @groupId integer;
? declare @name varchar(30);
? declare @descn varchar(100);
? declare @syntype varchar(4);
? declare @ddlsql varchar(1024);
? declare @isprimary char(1);
? declare @updateTime timestamp;
? declare @createTime timestamp;
? declare @createBy integer;
? declare @updateBy integer;
? declare @groupType integer;
? declare @adminType integer;
? declare @appId integer;
?
? declare @oldGroupId integer;
?
? set @groupId=NROW.GROUP_ID;
? set @name=NROW.name;
? set @descn=NROW.descn;
? set @syntype=NROW.syn_type;
? set @ddlsql=NROW.ddlsql;
? set @isprimary=NROW.isprimary;
? set @updateTime=NROW.update_time;
? set @createTime=NROW.create_time;
? set @createBy=NROW.create_by;
? set @updateBy=NROW.update_by;
? set @groupType=NROW.group_type;
? set @adminType=NROW.admin_type;
? set @appId=NROW.app_id;
?
? INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,
?
??????????
?CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,
?????????????
?@ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'INSERT');
? END;
?
? --
更新用戶組數(shù)據(jù)的觸發(fā)器
?-- DROP TRIGGER TG_USERG_UPDATE;
? CREATE TRIGGER TG_USERG_UPDATE AFTER UPDATE ON TM_USERG
?
??????????
?REFERENCING NEW AS NROW
?????????????
?FOR EACH ROW
?????????????
?MODE DB2SQL
?????????????
?BEGIN ATOMIC
?????????????
?
?????????????
?declare @groupId integer;
?
??????????
?declare @name varchar(30);
?
??????????
?declare @descn varchar(100);
?
??????????
?declare @syntype varchar(4);
?
??????????
?declare @ddlsql varchar(1024);
?
??????????
?declare @isprimary char(1);
?
??????????
?declare @updateTime timestamp;
?
??????????
?declare @createTime timestamp;
?
??????????
?declare @createBy integer;
?
??????????
?declare @updateBy integer;
?
??????????
?declare @groupType integer;
?
??????????
?declare @adminType integer;
?
??????????
?declare @appId integer;
?????????????
?
?????????????
?set @groupId=NROW.GROUP_ID;
?
??????????
?set @name=NROW.name;
?
??????????
?set @descn=NROW.descn;
?
??????????
?set @syntype=NROW.syn_type;
?
??????????
?set @ddlsql=NROW.ddlsql;
?
??????????
?set @isprimary=NROW.isprimary;
?
??????????
?set @updateTime=NROW.update_time;
?
??????????
?set @createTime=NROW.create_time;
?
??????????
?set @createBy=NROW.create_by;
?
??????????
?set @updateBy=NROW.update_by;
?
??????????
?set @groupType=NROW.group_type;
?
??????????
?set @adminType=NROW.admin_type;
?
??????????
?set @appId=NROW.app_id;
?????????????
?
?????????????
?--
如果已經(jīng)有
update
則只記錄最后一條
update
?????????????
?IF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='UPDATE') THEN
?????????????
?
???? UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,
?????????????
?
????
?
??????????? NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,
?????????????????????????????????? ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,
?????????????????????????????????? CREATE_TIME=@createTime,CREATE_BY=@createBy,
?????????????????????????????????? UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,
?????????????????????????????????? ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='UPDATE'
?????????????????????????????????? where GROUP_ID=@groupId AND ACTION='UPDATE';
?????????????
?--
如果有
insert
則把后面的
update
當(dāng)作
insert
????????????? ELSEIF? EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='INSERT') THEN
???????????????????? UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,
?????????????
?
????
?
??????????? NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,
?????????????????????????????????? ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,
?????????????????????????????????? CREATE_TIME=@createTime,CREATE_BY=@createBy,
?????????????????????????????????? UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,
?????????????????????????????????? ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='INSERT'
?????????????????????????????????? where GROUP_ID=@groupId AND ACTION='INSERT';
????????????? ELSE????? INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,
?
??????????
?
???????????
???CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,
?????????????
?
???????????
?? @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'UPDATE');
????????????? end if;
?????????????
?END;??????????
?
?
--
刪除用戶組觸發(fā)器
--DROP TRIGGER TG_USERG_DELETE;
CREATE TRIGGER TG_USERG_DELETE AFTER DELETE ON TM_USERG
??????
? REFERENCING OLD AS OROW
??????
? FOR EACH ROW
??????
? MODE DB2SQL
??????
? BEGIN ATOMIC
??????
?
??????
? declare @groupId integer;
?
???
??declare @name varchar(30);
?
???
??declare @descn varchar(100);
?
???
??declare @syntype varchar(4);
?
???
??declare @ddlsql varchar(1024);
?
???
??declare @isprimary char(1);
?
???
??declare @updateTime timestamp;
?
???
??declare @createTime timestamp;
?
???
??declare @createBy integer;
?
???
??declare @updateBy integer;
?
???
??declare @groupType integer;
?
???
??declare @adminType integer;
?
???
??declare @appId integer;
?????????????
?
??????
? set @groupId=OROW.GROUP_ID;
?
???
??set @name=OROW.name;
?
???
??set @descn=OROW.descn;
?
???
??set @syntype=OROW.syn_type;
?
???
??set @ddlsql=OROW.ddlsql;
?
???
??set @isprimary=OROW.isprimary;
?
???
??set @updateTime=OROW.update_time;
?
???
??set @createTime=OROW.create_time;
?
???
??set @createBy=OROW.create_by;
?
???
??set @updateBy=OROW.update_by;
?
???
??set @groupType=OROW.group_type;
?
???
??set @adminType=OROW.admin_type;
?
???
??set @appId=OROW.app_id;
??????
?
??????
?? --
如果沒(méi)有操作記錄,則插入
delete
記錄
??????
?? IF NOT EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId) THEN
??????
??
?
??INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,
?
??????????
?CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,
?????????????
?@ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'DELETE');
?????????????
?
?????????????
?--
如果有
insert
記錄,則整體結(jié)果相當(dāng)于沒(méi)有進(jìn)行任何操作
?????????????
?ELSEIF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT') THEN
?????????????
?
??????????? DELETE FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT';
?????????????
?--
如果沒(méi)有
insert
記錄,則只需記錄最后的
delete
操作
?????????????
?ELSE
?????????????
?
????
? UPDATE TM_USERG_TEMP set ACTION='DELETE' where GROUP_ID=@groupId;
??????
?? END IF;
??????
??
??????
?? END;
在項(xiàng)目中碰到一些重用上的問(wèn)題,有些想法,就先寫(xiě)一點(diǎn)。
重用應(yīng)該是高層的復(fù)用,邏輯的復(fù)用,接口的復(fù)用,而不是具體實(shí)現(xiàn)的復(fù)用。
我們項(xiàng)目開(kāi)始講復(fù)用,就是大家把別人的代碼拿過(guò)來(lái),可用的地方就用,不同的地方改改,結(jié)果問(wèn)題一堆。說(shuō)到底就是接口沒(méi)有定義清楚的,很多該復(fù)用的邏輯隱藏在了具體的實(shí)現(xiàn)中。這樣導(dǎo)致無(wú)法進(jìn)行接口的復(fù)用,轉(zhuǎn)而使用具體的實(shí)現(xiàn)復(fù)用。從程序員的角度看,他們總會(huì)使用成本最小的方法完成任務(wù)。所以我們要時(shí)刻思考如何能讓最正確的方法在他們看來(lái)同時(shí)也是成本最小。
這里有一個(gè)較為簡(jiǎn)單的辦法,就是盡量使用方法封裝實(shí)現(xiàn),使接口的粒度最小。如果一個(gè)實(shí)現(xiàn)需要幾百行,且其中包含多個(gè)邏輯,就最好抽取出多個(gè)方法,然后在主體接口內(nèi)進(jìn)行調(diào)用。這樣的代碼邏輯清晰易讀,可重用性也高。看看大師們對(duì)代碼的不斷重構(gòu),很大程度上就是重構(gòu)出粒度最細(xì),復(fù)用性最高的接口。
如何達(dá)到最大程度的復(fù)用,其實(shí)是非常復(fù)雜的問(wèn)題,還需要在今后的項(xiàng)目中不斷體會(huì)。
最近做
portal
的壓力測(cè)試,一個(gè)字“累”。其中犯了不少錯(cuò)誤,白白加了幾天班,也有一些體會(huì),就記錄下來(lái),希望對(duì)大家有所幫助。
首先講壓力測(cè)試環(huán)境。這個(gè)很是關(guān)鍵,我們就是在這個(gè)上面吃了苦頭。我們用的
loadrunner
,原理也很簡(jiǎn)單,一臺(tái)主控機(jī),控制多臺(tái)客戶機(jī),模擬并發(fā)用戶訪問(wèn)應(yīng)用。然后需要能實(shí)時(shí)監(jiān)控各相關(guān)應(yīng)用服務(wù)器,
ldap
服務(wù)器等的性能。這里每臺(tái)客戶機(jī)最好能使用同樣的配置,使用足夠帶寬的網(wǎng)絡(luò),給予同樣的負(fù)載(模擬同樣數(shù)量的用戶)。同時(shí)要注意監(jiān)控客戶機(jī)的
cpu
和網(wǎng)絡(luò)狀況,時(shí)刻保證
cpu
和網(wǎng)絡(luò)利用率低于
100%
。我們犯的很大錯(cuò)誤就是使用各自的筆記本,而且都使用的是一個(gè)
10M hub
牽出的網(wǎng)線,這樣導(dǎo)致實(shí)際的網(wǎng)絡(luò)阻塞,既沒(méi)有給予服務(wù)器足夠的負(fù)載,又導(dǎo)致報(bào)告的響應(yīng)時(shí)間比實(shí)際更長(zhǎng),從而帶來(lái)了后續(xù)很多的無(wú)用測(cè)試。
然后講測(cè)試方法。用的較多的還是持續(xù)壓力測(cè)試,就是持續(xù)給予服務(wù)器一段時(shí)間的并發(fā)量(一般為
5
到
10
分鐘),然后看平均響應(yīng)時(shí)間是否在可以接受的范圍內(nèi)。這個(gè)“可接受”要視應(yīng)用類型和實(shí)際的并發(fā)用戶而定,如何估計(jì)并發(fā)就要靠經(jīng)驗(yàn)了。對(duì)于
portal
而言,由于要與眾多的應(yīng)用接口,如進(jìn)行
SSO
,獲取數(shù)據(jù)等,有很大程度也依賴于其它應(yīng)用的性能,其性能要求不會(huì)太高。我們測(cè)試首頁(yè)的性能,在放上全部的
portlet
的情況下,
100
個(gè)并發(fā)的平均響應(yīng)時(shí)間就在
17s
左右,這肯定是不能接受的(
10s
只能算勉強(qiáng)可以)。接下來(lái)就是發(fā)現(xiàn)性能瓶頸,并嘗試進(jìn)行優(yōu)化了。
初步的發(fā)現(xiàn)瓶頸的方法也很簡(jiǎn)單,通過(guò)對(duì)
portlet
的增減,發(fā)現(xiàn)最影響性能的
portlet
,然后不斷優(yōu)化,直至達(dá)到可以接受范圍。發(fā)現(xiàn)瓶頸所在了,就得進(jìn)一步確定是什么原因:是我們本身程序的問(wèn)題,還是其它應(yīng)用接口性能不佳等等。這里光靠猜是不行的,要講數(shù)據(jù)講事實(shí),記錄時(shí)間日志就是簡(jiǎn)單有效的辦法,我們對(duì)各個(gè)時(shí)間點(diǎn)打印了日志,比如
doview
方法的全部執(zhí)行時(shí)間,
jsp
的載入時(shí)間,具體接口的執(zhí)行時(shí)間等。有些接口可能在壓力較小的情況下性能不錯(cuò),而在大壓力情況下出現(xiàn)性能隱患,所以一定要在進(jìn)行壓力測(cè)試后查看日志。我們就是這樣發(fā)現(xiàn)了性能隱患,同時(shí)更進(jìn)一步對(duì)各方面進(jìn)行優(yōu)化,直至達(dá)到客戶可以接受為止。
由于不是專門(mén)的測(cè)試人員,很多地方都是實(shí)際項(xiàng)目中的體會(huì),也沒(méi)什么理論基礎(chǔ)。有什么不對(duì)的地方,大家多交流。
接著昨天的寫(xiě)。今天寫(xiě)我認(rèn)為的一個(gè)
javaEE
項(xiàng)目中應(yīng)該提倡的做法。
1.??????
開(kāi)發(fā)流程盡量簡(jiǎn)化,采用迭代增量的模式,做適合項(xiàng)目需要的文檔。很多時(shí)候千言不如一圖,原型開(kāi)發(fā)我認(rèn)為也非常重要。
2.??????
采用成熟的框架,
ssh
組合或更多
full-stack
的框架如
seam
等都是不錯(cuò)的選擇。如果一定要用公司的框架,至少
SA
要非常熟悉這個(gè)框架,在出現(xiàn)問(wèn)題時(shí)要能快速的解決。
3.??????
對(duì)業(yè)務(wù)的分析做到越細(xì)越好,如果有條件讓更多的開(kāi)發(fā)人員參與業(yè)務(wù)的分析,同時(shí)形成項(xiàng)目通用的業(yè)務(wù)語(yǔ)言(實(shí)在不行,精簡(jiǎn)的
user story
也可以)。對(duì)于每個(gè)達(dá)成共識(shí)的業(yè)務(wù)都要能記錄下來(lái),并能方便的進(jìn)行查閱。業(yè)務(wù)模型和業(yè)務(wù)規(guī)則要始終與當(dāng)前需求、代碼和數(shù)據(jù)庫(kù)保持一致。
4.??????
在團(tuán)隊(duì)的建設(shè)上,需要更多的投入。不要為了節(jié)約成本,讓很多程序員老后面才加入團(tuán)隊(duì)。一個(gè)穩(wěn)定、團(tuán)結(jié)、有沖勁的團(tuán)隊(duì)能比松散而人數(shù)更多的團(tuán)隊(duì),完成的更快更好。然后要加強(qiáng)溝通,比如每天開(kāi)個(gè)小的茶話會(huì),大家交流下各自的工作情況,有什么困惑和疑難,提出來(lái)大家一起解決,避免大家各自做相同的邏輯(很多東西經(jīng)過(guò)抽象可能就是一個(gè))。在工作之余大家一塊吃吃飯,打打游戲等都是增進(jìn)感情的好方法,大家彼此熟悉了,工作上也能更好的協(xié)作。
5.??????
對(duì)程序員要有更高的要求,
SA
有責(zé)任讓程序員了解更多的東西,如面向?qū)ο蟮?/span>
5
大原則、一些模式、
junit
、重構(gòu)等,這些其實(shí)并不是什么高深的東西,僅僅是掌握一些方面也能對(duì)代碼質(zhì)量和開(kāi)發(fā)中的愉悅度產(chǎn)生很大促進(jìn)。要激發(fā)他們對(duì)技術(shù)的熱愛(ài)和對(duì)代碼質(zhì)量的追求,因?yàn)樽罱K受益的還是他們。
XP
所提倡的結(jié)對(duì)編程也是快速進(jìn)行知識(shí)傳遞的好辦法。
6.??????
采用
wiki
進(jìn)行項(xiàng)目進(jìn)度跟蹤和一些文檔的展示。這次用
excel+cvs
的方式感覺(jué)很是麻煩,在
spring
翻譯中我們采用
wiki
的方式就感覺(jué)很好。
暫時(shí)先想到這么多,有更多體會(huì),再來(lái)補(bǔ)充!
摘要: 在程序員發(fā)表的一篇maven文章,跟大家共享。用
Maven
做項(xiàng)目管理
在
Java世界中我們很多的開(kāi)發(fā)人員選擇用
Ant來(lái)構(gòu)建項(xiàng)目,一個(gè)
build.xml能夠完成編譯、測(cè)試、打包、部署等很多任務(wù),但我們...
閱讀全文
開(kāi)發(fā)進(jìn)行到尾聲了,但
bug
仍然層出不窮。總的來(lái)說(shuō),算是一個(gè)比較失敗的項(xiàng)目,原因很多,有外在因素也有我作為一個(gè)
SA
不可推卸的責(zé)任。正好借加班的時(shí)間寫(xiě)點(diǎn)總結(jié),也算是在失敗總吸取教訓(xùn),從錯(cuò)誤中感受更多吧。
首先是開(kāi)發(fā)流程。我是
xp
的堅(jiān)定支持者,但在項(xiàng)目中由于外界原因還是采用了傳統(tǒng)的開(kāi)發(fā)流程,沒(méi)有迭代,就是需求
->
設(shè)計(jì)
->
程序員進(jìn)場(chǎng)開(kāi)發(fā)
->
改
bug
。由于程序員進(jìn)場(chǎng)時(shí)間較晚,一上來(lái)就開(kāi)始開(kāi)發(fā),沒(méi)有時(shí)間進(jìn)行培訓(xùn)和團(tuán)隊(duì)的融合。然后開(kāi)發(fā)中缺少溝通,就是一個(gè)人負(fù)責(zé)一塊,開(kāi)發(fā)完了再做其它。結(jié)果開(kāi)發(fā)到現(xiàn)在,還有人不清楚我們項(xiàng)目的全貌,到底是為了解決什么業(yè)務(wù)。
然后是開(kāi)發(fā)框架。使用了公司的框架,而我們作為
SA
(我們是雙
SA
),都是第一次接觸,程序員也就一個(gè)人用過(guò)。我們最早是達(dá)成共識(shí)采用
SSH
的組合(我至少還算是了解吧,其它人也都用過(guò)),但由于上層因素沒(méi)有實(shí)施(這也導(dǎo)致我好長(zhǎng)一段時(shí)間進(jìn)入不了狀態(tài))。開(kāi)發(fā)前期大家都在探索這個(gè)框架(的確很難用,出錯(cuò)機(jī)制較差,配置文件很多,耦合較強(qiáng)
...
),在一堆莫名奇妙的問(wèn)題中摸索前行,花費(fèi)大量的精力。而比較搞笑的是,在大家開(kāi)始學(xué)習(xí)這個(gè)框架之時(shí),我作為
SA
,因?yàn)橐獙?xiě)一堆只為應(yīng)付客戶的設(shè)計(jì)文檔(后面就沒(méi)人看過(guò)),錯(cuò)過(guò)了和大家共同進(jìn)步的機(jī)會(huì),后面總是感覺(jué)“低人一等”。
在業(yè)務(wù)方面也存在很多問(wèn)題。很多業(yè)務(wù)邏輯并沒(méi)有以很好的載體保存下來(lái),在需求文檔中很多邏輯并沒(méi)有體現(xiàn)。我維護(hù)了一套
pd
的業(yè)務(wù)模型,從概念模型
->
物理模型
->
數(shù)據(jù)庫(kù),這解決了后面的一些溝通問(wèn)題,但由于更多體現(xiàn)的是靜態(tài)的實(shí)體及關(guān)聯(lián),對(duì)于一些動(dòng)態(tài)的業(yè)務(wù)流程沒(méi)法體現(xiàn)。我們
SA
之間有時(shí)在一些問(wèn)題上的理解還存在分歧(討論過(guò)也達(dá)成過(guò)共識(shí),但沒(méi)有記錄下來(lái),后面可能就忘了),程序員就更是無(wú)所適從。談到這,我更感受到
DDD
這本書(shū)的價(jià)值,他所提倡的開(kāi)發(fā)人員參加模型的討論,形成項(xiàng)目的模型語(yǔ)言,并不斷隨著業(yè)務(wù)進(jìn)行演化。。。好多理念都是項(xiàng)目經(jīng)驗(yàn)的結(jié)晶啊。
在開(kāi)發(fā)管理上我也是無(wú)所作為。
Junit
都沒(méi)有推廣下去,更別說(shuō)
TDD
了,這也與框架相關(guān),它就沒(méi)提供寫(xiě)
test case
的地方,等我搞明白一堆配置文件,做出脫離
web
容器的
test
框架,都開(kāi)發(fā)一大半了,說(shuō)起
test
的好處,大家也表示不理解(或者表示理解但沒(méi)時(shí)間
=
沒(méi)理解),就讓他們慢慢
debug
吧!代碼的質(zhì)量也沒(méi)有保證,程序員不明白代碼的味道,更別說(shuō)理解重構(gòu)的意義以及進(jìn)行恰當(dāng)?shù)闹貥?gòu)了。一個(gè)函數(shù)寫(xiě)上
100
多行,什么邏輯都混在一塊,但由于時(shí)間較緊,我也只好睜一只眼閉一只眼“功能完成就行吧!也不是我一個(gè)人在管”,到現(xiàn)在很多代碼混成一團(tuán),展現(xiàn)層直接調(diào)用
dao
(又是框架惹得禍),相同的邏輯
copy
到
n
處,我也是后悔莫及。
今天先寫(xiě)失誤,明天寫(xiě)從中學(xué)到的東西,從錯(cuò)誤中學(xué)到的也許更多!
最近看了看領(lǐng)域模型驅(qū)動(dòng)這本書(shū),只看了前面幾章,但也深切的感受到了模型的重要性。通過(guò)與代碼同步的模型,能夠維護(hù)一個(gè)很好的知識(shí)共享的空間,包括設(shè)計(jì)者與程序員之間,客戶與設(shè)計(jì)者之間
……
而且模型應(yīng)該盡可能簡(jiǎn)單,讓不同背景的人都能夠很快學(xué)會(huì),并都能對(duì)模型有所增益。
那么這個(gè)模型應(yīng)該是什么樣的?書(shū)我沒(méi)有細(xì)看,只說(shuō)說(shuō)自己的體會(huì)。關(guān)于設(shè)計(jì),很早就有數(shù)據(jù)驅(qū)動(dòng)和對(duì)象驅(qū)動(dòng)的提法。在
Without EJB
里,
Rod
也有講:數(shù)據(jù)驅(qū)動(dòng)或者說(shuō)面向數(shù)據(jù)庫(kù)設(shè)計(jì)更成熟,工具更多;而對(duì)象驅(qū)動(dòng)更符合面向?qū)ο蟪绦虻奶匦裕捎谡莆盏娜溯^少,風(fēng)險(xiǎn)較大。而通過(guò)模型驅(qū)動(dòng),我認(rèn)為很大程度填補(bǔ)了
2
種方式的鴻溝,核心是模型,具體是對(duì)象模型還是數(shù)據(jù)模型并不重要,重要的是這個(gè)模型能夠與需求、代碼、數(shù)據(jù)庫(kù)保持一致。
說(shuō)到這里,順便談一談我對(duì)文檔的理解。我一直是
XP
的堅(jiān)定支持者,甚至有點(diǎn)偏執(zhí)。而由于文檔不易閱讀和溝通,且經(jīng)常會(huì)出現(xiàn)與設(shè)計(jì)和代碼的脫節(jié),導(dǎo)致其可讀性更差,所以我一向?qū)ξ臋n不大感冒,更傾向于使用代碼說(shuō)話。但在目前的公司項(xiàng)目中,由于更多采用傳統(tǒng)的軟件過(guò)程,我也寫(xiě)了很多的文檔,包括需求規(guī)格說(shuō)明書(shū)、概要設(shè)計(jì)文檔、詳細(xì)設(shè)計(jì)文檔等等。從對(duì)項(xiàng)目的幫助來(lái)看,文檔作用并不太大,或者說(shuō)是付出收益比太低,更多的是給客戶寫(xiě)的,而不是給程序員寫(xiě)的。從程序員的需要來(lái)看,他關(guān)心的是每個(gè)實(shí)體的屬性和關(guān)聯(lián),核心的接口、輸入和輸出,頁(yè)面間的跳轉(zhuǎn)和數(shù)據(jù)流,然后有一個(gè)統(tǒng)一的框架和編程模式。我的體會(huì)是:如果以文檔為核心,很難描述清楚這些東西,且難以應(yīng)對(duì)變化。
而通過(guò)以模型為核心(項(xiàng)目現(xiàn)在采用的
power designer
的概念模型為基礎(chǔ)),輔以適當(dāng)?shù)拿枋觯饶軌蚣涌齑蠹覍?duì)項(xiàng)目的認(rèn)識(shí)(程序員是后面才加入),又能夠節(jié)省一些寫(xiě)文檔的時(shí)間,更早投入開(kāi)發(fā)。
說(shuō)到
power designer
,我也比較慚愧。用了好久,一直只是把它當(dāng)成看數(shù)據(jù)庫(kù)的工具。項(xiàng)目一開(kāi)始就是從物理模型入手,結(jié)果舉步維艱。后面從概念模型入手,就感受到了它的好處。使用概念模型,不用考慮太多關(guān)聯(lián)表、外鍵什么的,而是從實(shí)體出發(fā),然后確定相互間的關(guān)聯(lián),是一對(duì)一、一對(duì)多還是多對(duì)多。然后自動(dòng)轉(zhuǎn)成物理模型,并直接與相應(yīng)的數(shù)據(jù)庫(kù)掛鉤。從這點(diǎn)上看與從對(duì)象設(shè)計(jì)出發(fā)真的非常相似。其實(shí)這也是合情合理的,正體現(xiàn)了這個(gè)世界的統(tǒng)一性吧(物理學(xué)界不也在搞什么統(tǒng)一場(chǎng)理論的證明嗎)。
Power designer
也做了
conceptual model, physical model, object-oriented model
和
xml model
的自動(dòng)轉(zhuǎn)換,我現(xiàn)在還沒(méi)全部摸熟。
openfans
則是從對(duì)象入手,并通過(guò)
hibernate
建立與數(shù)據(jù)庫(kù)的聯(lián)系,也體現(xiàn)了一定的方便靈活性。但比較糟糕的是,只有代碼和配置文件,沒(méi)有清晰的便于交流的模型,誰(shuí)要想?yún)⑴c只能先去慢慢看代碼。所以我先通過(guò)
together reverse
出來(lái)一個(gè)類圖,然后適當(dāng)加以文字進(jìn)行說(shuō)明。類圖已經(jīng)做好,但比較亂,還需要更多的圖例加以說(shuō)明。文字說(shuō)明就是下一篇
blog
的工作了。也算是預(yù)告吧!
近來(lái)在一個(gè)項(xiàng)目做
SA
,也是第一次做比較大的項(xiàng)目的設(shè)計(jì),感覺(jué)比較吃力。同時(shí)又要參與
spring
文檔的翻譯,一直沒(méi)時(shí)間寫(xiě)
blog
。今天終于有點(diǎn)時(shí)間,就寫(xiě)一下最近的感悟。
首先是不適應(yīng)。要參與需求階段,因?yàn)樾枨蟪跗诓⒉淮_定,客戶都不清楚他們需要什么東西,只是有一個(gè)很模糊的概念。我們得不斷調(diào)研、討論、出方案、出原型
……
而這都是我比較不擅長(zhǎng)的。還好有個(gè)職務(wù)較高的老大帶著我們,才能逐漸把需求理順。我也從他身上學(xué)到不少,準(zhǔn)備寫(xiě)一篇“如何做需求”,但畢竟是第一次做較大的需求,理解還不很深刻,怕貽笑大方,所以只拿
MindManager
列了個(gè)提綱。
其次還是不適應(yīng)。項(xiàng)目開(kāi)始好幾個(gè)月,沒(méi)寫(xiě)過(guò)一行代碼。項(xiàng)目沒(méi)有采用
XP
的方式,而是普通的瀑布。需求就做了幾個(gè)月,然后做概設(shè)、詳設(shè)。我是
XP
的支持者,所以對(duì)這種方式持反對(duì)態(tài)度,但老大不同意,沒(méi)辦法!寫(xiě)文檔,我也是很不情愿,但轉(zhuǎn)念一想:
Rod
寫(xiě)
Without EJB
,但他
ejb
的理解比誰(shuí)都深,什么方式都實(shí)踐下可能更好。由于同時(shí)在看
Joel on software
,他對(duì)需求規(guī)格說(shuō)明書(shū)卻很是強(qiáng)調(diào),我也就聽(tīng)聽(tīng)大師的話,好好寫(xiě)需求,順便把他的一招用上了
-------
寫(xiě)的有趣點(diǎn),就當(dāng)寫(xiě)故事吧。
最后還是不適應(yīng)。以前做程序員,可以好好研究很多東西,現(xiàn)在不行了。有個(gè)
xml
與
bean
轉(zhuǎn)換的技術(shù)要解決,我能研究不?不行,我得寫(xiě)文檔,這種比較
detail
的事情得給程序員做。看著程序員興高采烈的比較各種開(kāi)源工具,最后選定
JIBX
(
openfans
發(fā)揮了一定的作用),然后跟我講這個(gè)如何如何好,我只有附和的份。
講到這里,讓我想到一則小故事:有一個(gè)學(xué)鋼琴的拜一個(gè)牛人為師。牛人交給他一個(gè)曲譜,說(shuō):“回去練好,一個(gè)月再過(guò)來(lái)。”他好歹把這個(gè)曲練熟了,還想展示一下,牛人又交給他一個(gè)更難的曲譜,又是同樣的話。他只好回去繼續(xù)苦練,每次都感覺(jué)不適應(yīng)。這樣往返多次,他忍不住了,問(wèn)牛人:“你是不是故意整我,每次都給我更難的,還不給我表現(xiàn)的機(jī)會(huì)”。牛人讓他把上次的曲彈彈,他感覺(jué)不錯(cuò),讓彈再上次的,更是輕松,最后彈第一次,他彈的是出神入化。他明白了!
大家都明白沒(méi):只有不斷的感到不適應(yīng),才能進(jìn)步。如果一切感覺(jué)良好,沒(méi)什么挑戰(zhàn),就該考慮。。。。。。(此處省略
2
字)了。
不經(jīng)意看到了程序員的一期算法專題,細(xì)細(xì)研讀多位高手(包括李開(kāi)復(fù))的文字之后,對(duì)算法的重要性重新進(jìn)行了反思。我研究生畢業(yè)
2
年,一直從事
J2EE
開(kāi)發(fā),由于項(xiàng)目的原因,很少需要自己去設(shè)計(jì)算法,甚至
stack
,
tree
這些數(shù)據(jù)結(jié)構(gòu)都很少使用。還好自己也不甘于平淡,如
Effective Java
,
Practical Java
,
Refactory
,
Design Pattern
等等這些流行書(shū)還是抽空學(xué)習(xí),這些書(shū)的確很是經(jīng)典,對(duì)我的編碼風(fēng)格,模式的理解,設(shè)計(jì)能力都起到了很好的促進(jìn)。也快速的由一個(gè)程序員成長(zhǎng)為架構(gòu)師(只是公司的,離真正的架構(gòu)師還差得遠(yuǎn))。
因?yàn)轫?xiàng)目需要,去年下半年開(kāi)始全面接觸開(kāi)源軟件,使用了
spring
,
maven
,
hibernate
,
ibatis
等眾多開(kāi)源軟件,也對(duì)開(kāi)源軟件產(chǎn)生了濃厚的興趣,于是拿這些開(kāi)源軟件做了
openfans
,一方面是推進(jìn)開(kāi)源軟件在中國(guó)的使用的交流,一方面也為自己在實(shí)踐中更多使用這些軟件(因?yàn)闆](méi)有項(xiàng)目和利益因素,可以做想做的事,用想用的軟件)。使用這些開(kāi)源軟件倒很是順利,很多軟件拿來(lái)就能用,都有
sample
,簡(jiǎn)單使用還是不難的。
但一些關(guān)鍵的問(wèn)題一直懸而未決!比如
tag
的設(shè)計(jì):我現(xiàn)在簡(jiǎn)單的使用平鋪的模型,
tag
沒(méi)有層次之分,
tag
間產(chǎn)生雙向關(guān)聯(lián)。但這樣是最符合
tag
特性的模型嗎?如何對(duì)這些
tag
進(jìn)行分類,如何定義
tag
的多級(jí)關(guān)聯(lián)(如
spring
和
hibernate
有關(guān)聯(lián),
hibernate
又與持久層關(guān)聯(lián),
spring
是否與持久層有間接關(guān)聯(lián),依次類推)。。。。。。而做出一個(gè)好的
tag
模型,可能就需要圖論方面的知識(shí)。再比如用戶相似度設(shè)計(jì)(號(hào)稱是豆瓣的核心,難以復(fù)制):每個(gè)用戶擁有了一些
tag
,如何根據(jù)這些
tag
定義用戶的相似度,一個(gè)用戶有
spring
,
hibernate
這
2
個(gè)
tag
,一個(gè)用戶有
spring
,
ibatis
這
2
個(gè)
tag
,他們相似度為多少,如果每個(gè)人
tag
都很多,再加上權(quán)重的概念,問(wèn)題又復(fù)雜的多。簡(jiǎn)單的做法就是每個(gè)用戶
tag
一個(gè)個(gè)匹配,匹配的越多相似度越大,但這樣設(shè)計(jì)一是不準(zhǔn)確,二是時(shí)間復(fù)雜度很大,最壞情況為
n*n*m*m
,
n
為用戶數(shù),
m
為每個(gè)用戶的
tag
數(shù)。
這些都需要扎實(shí)的算法基礎(chǔ)。而我的基礎(chǔ)就很薄弱:本科學(xué)的比文科還文科的專業(yè),研究生又學(xué)的比較上層的東西(
UML
,
RUP
,
PM
等,也都一知半解),選修了一門(mén)算法導(dǎo)論,又被
1000
多頁(yè)的經(jīng)典英文教材嚇趴下了,上了幾次課就直接放棄,沒(méi)敢參加最后考試。現(xiàn)在想臨時(shí)抱佛腳,談何容易。
所以算法也并非沒(méi)有用處,關(guān)鍵要看你在做什么,想做什么。想去
google
、百度不用會(huì)
spring
,算法基礎(chǔ)扎實(shí),只會(huì)
c
語(yǔ)言都行;一些行業(yè)如電信、金融也很是需要算法高手。而國(guó)內(nèi)更多的企業(yè)做企業(yè)應(yīng)用,一般是連連數(shù)據(jù)庫(kù),寫(xiě)寫(xiě)頁(yè)面,最多引入些開(kāi)源框架和軟件,如
spring
,
hibernate
,
struts
等。這方面的需求較大,會(huì)了
spring
,省了公司的培訓(xùn)成本,自然還是給找工作加了一些砝碼。
所以有時(shí)聽(tīng)到某些人對(duì)某項(xiàng)技術(shù)不以為然,說(shuō)“這些東西有什么是我在幾個(gè)星期學(xué)不會(huì)的”的時(shí)候,一方面是對(duì)其狂妄進(jìn)行些鄙視,一方面也真要問(wèn)問(wèn)自己,我的核心價(jià)值到底在哪。這個(gè)問(wèn)題很重要,涉及面很廣,選擇也很多,而我也只是有些模糊的答案,等以后再仔細(xì)寫(xiě)寫(xiě)。
不管如何,我是要開(kāi)始研究算法了,得解決問(wèn)題阿!先在
openfans
開(kāi)個(gè)算法的
tag
,一邊學(xué)一邊積累,對(duì)算法有興趣的同學(xué)也可以跟我一塊進(jìn)步。
PS
:做個(gè)廣告,
blogjava
很多好的
bloger
,能否到
www.openfans.net
導(dǎo)入下
blog
,跟大家分享下你的感悟,謝謝!
很不好意思,不是原創(chuàng)技術(shù)。做個(gè)廣告,有不妥,歡迎管理員從首頁(yè)拿掉。
??? 你是開(kāi)源軟件的愛(ài)好者,平時(shí)學(xué)習(xí)和使用這些軟件,也不時(shí)寫(xiě)寫(xiě)
blog
,記下些心得。
你是開(kāi)源軟件的傳播者,你希望更多的人了解和使用開(kāi)源軟件,希望你的文章被更多的人閱讀,并展開(kāi)更深刻的討論。
你是開(kāi)源軟件的參與者,平時(shí)參與參與國(guó)外的開(kāi)源項(xiàng)目,也希望中國(guó)能有更多的開(kāi)源團(tuán)體,大家一起做國(guó)人自己的開(kāi)源軟件。。。。。。
只要你對(duì)開(kāi)源軟件保持著一份熱愛(ài),歡迎來(lái)到
openfans(www.openfans.net)
。
非常方便的注冊(cè)后,你就可以點(diǎn)擊“提交
feed
”,只要輸入你的
rss
地址(由于時(shí)間原因,還沒(méi)做直接從
web
地址發(fā)現(xiàn)
feed
),就可以將你的
feed
加入,同時(shí)我們對(duì)一些網(wǎng)站提供了簡(jiǎn)單的匹配(如
blogjava
,只需輸入你在
blogjava
的用戶名,系統(tǒng)會(huì)自動(dòng)匹配成你在
blogjava
的
feed
)。完成后,點(diǎn)擊“立即導(dǎo)入”,就可以將你的文章入庫(kù),點(diǎn)擊“最新日志”可以查看。以后系統(tǒng)會(huì)每日定期讀取你的
feed
,自動(dòng)將新的文章加入。由于你提供的是
rss
,內(nèi)容應(yīng)該是文章的簡(jiǎn)短描述(視你的
blog
提供商而定),而且我們會(huì)為每篇文章提供原文鏈接,直接指向你的
blog
原文。
導(dǎo)入的日志一般是沒(méi)有進(jìn)行分類的,不方便大家的查找。在每篇日志上都有個(gè)“我要推薦”鏈接,點(diǎn)擊并輸入你認(rèn)為適合的標(biāo)簽(如
spring
,
hibernate
,
cms
)等,就可以把這篇日志形成文章,放在相應(yīng)的標(biāo)簽下,永久保存。需要學(xué)習(xí)
spring
,
hibernate
,
cms
的后來(lái)者,可以方便的查找到標(biāo)簽和軟件,找到你的文章,進(jìn)而進(jìn)入你的
blog
。
同時(shí)你也可以發(fā)表文章,推薦軟件,創(chuàng)建和加入小組,進(jìn)行評(píng)論。。。。。。我們會(huì)不斷完善功能,給大家提供更方便的功能和更好的用戶體驗(yàn)。
由于現(xiàn)在人員較少,開(kāi)發(fā)進(jìn)度較慢。但先做個(gè)廣告,下一步會(huì)做
digg
,提供對(duì)軟件、文章、用戶等的
digg
。做對(duì)一些標(biāo)簽的
rss
,如
springframework
網(wǎng)站的
rss
,自動(dòng)獲取
spring
的版本更新信息。還要完善小組功能和好友功能,給大家提供一個(gè)方便交流的平臺(tái)。
網(wǎng)站拿
java
的一堆開(kāi)源軟件做成,同時(shí)本身也是開(kāi)源軟件,希望參與的同學(xué)可以
email
給
pesome@gmail.com
,大家一塊為推動(dòng)開(kāi)源軟件在中國(guó)的發(fā)展做出自己的貢獻(xiàn)。
? 前面openfans用的JDK1.4,今天下決心換成1.5了。運(yùn)行倒是好好的,在jetty下也沒(méi)有什么問(wèn)題。一不小心點(diǎn)了下eclipse里我做的mvn eclipse:eclipse的External Tools,就開(kāi)始maven了。停也沒(méi)用了,等著吧。結(jié)果報(bào)錯(cuò):D:\javaproject\openfans\main\src\org\openfans\domain\Group.java:[29,19] -source 1.3 中不支持泛型(請(qǐng)嘗試使用 -source 1.5 以啟用泛型)。看了看maven的bat,會(huì)自動(dòng)使用環(huán)境變量配置的jdk,應(yīng)該沒(méi)問(wèn)題啊。還好我網(wǎng)上認(rèn)識(shí)人多,想起alin用的jdk1.5,就問(wèn)他怎么回事。發(fā)過(guò)來(lái)這個(gè):
<
plugin
>
??????
<
artifactId
>
maven-compiler-plugin
</
artifactId
>
?????????????
<
configuration
>
?????????????????
<
source
>
1.5
</
source
>
?????????????????
<
target
>
1.5
</
target
>
?????????????
</
configuration
>
</
plugin
>
我一看就明白了,
mvn
時(shí)是用
1.3
給我編譯的,得告訴它用
1.5
。拷到
pom
文件中,再
mvn eclipse:eclipse
搞定。問(wèn)題是很快解決了,同時(shí)卻留下了很多思考:
1.
技術(shù)沒(méi)有止境,做人一定要謙虛。
Maven2
我用的也算比較早,還曾經(jīng)被白衣說(shuō)是對(duì)maven2的推廣做了貢獻(xiàn)的,自己也頗以為然。而現(xiàn)在這個(gè)簡(jiǎn)單的問(wèn)題卻不知道了,還得google或問(wèn)人解決。還好我一直比較謙虛(本身也沒(méi)啥可驕傲的資本),否則要狂被鄙視了。
2.
技術(shù)的推廣要不遺余力,好的東西要讓大家都知道。
Maven2
我也只是使用,了解并不深入(項(xiàng)目中碰到了的知道,沒(méi)碰到的就不懂了),但我是到處推薦,碰到個(gè)人就說(shuō)這個(gè)好。這下很多朋友都知道了,也引入項(xiàng)目實(shí)踐了。一方面他們用的舒服,提高了效率,有點(diǎn)問(wèn)題還可以向我這個(gè)所謂的maven2高手請(qǐng)教,我自是“知無(wú)不言,言無(wú)不盡”;另一方面,他們也許就碰到其它問(wèn)題了,然后知道如何解決,在我碰到類似問(wèn)題時(shí),就可以向他們請(qǐng)教了。你看,多好的良性循環(huán),想想都美滋滋的。
3.
多進(jìn)行知識(shí)共享,大家的智慧比個(gè)人強(qiáng)。
這是從更高的角度看了,通過(guò)知識(shí)的共享,能迅速集合大家的經(jīng)驗(yàn)和智慧,讓個(gè)體更快的進(jìn)行學(xué)習(xí),少走彎路。你共享自己知識(shí)的同時(shí),也能獲得別人的成果。如果你知道誰(shuí)spring比較強(qiáng),誰(shuí)hibernate比較強(qiáng),誰(shuí)在用maven,而且碰到問(wèn)題能看他們的文章或直接向他們請(qǐng)教,做起項(xiàng)目來(lái)是不是都安心的多。可能有人說(shuō)有google,但google信息量太大,而且很多文章是處處轉(zhuǎn)載千篇一律,經(jīng)常半天找不到東西。我是深有體會(huì),所以想到做openfans,做一個(gè)知識(shí)共享的平臺(tái),并做到去糟取精。現(xiàn)在還遠(yuǎn)遠(yuǎn)達(dá)不到要求,但我會(huì)努力的。
領(lǐng)域模型驅(qū)動(dòng)(
Domain Driven Design
),很熱的名詞。
Openfans
,不太熱的網(wǎng)站。今天俺就借著很熱的
ddd
,給不太熱的
openfans
再造點(diǎn)勢(shì)。
Openfans
就不多介紹了,網(wǎng)站用
spring+hibernate
為核心的一堆開(kāi)源軟件構(gòu)建。有了
spring
的
IOC
和
hibernate
的
ORM
,打著
ddd
的旗號(hào)也就名正言順了很多。先聲明其實(shí)俺對(duì)
ddd
的理解也多是道聽(tīng)途說(shuō),沒(méi)有什么系統(tǒng)的學(xué)習(xí)過(guò),倒是和
Joe
阿牛討論過(guò)幾次,頗有受益,他對(duì)這個(gè)理解還是很深刻的。
言歸正傳,就講
openfans
現(xiàn)在經(jīng)
ddd
思想改造過(guò)的模型。整體上看還是普通的三層架構(gòu)體系:展現(xiàn)層、業(yè)務(wù)層、持久層。展現(xiàn)層用
spring mvc
,力圖做到只是展示相關(guān),避免出現(xiàn)業(yè)務(wù)邏輯。再具體細(xì)分,就是
jsp
頁(yè)面只有展示邏輯,主要使用
jstl
完成顯示功能。
Controller
負(fù)責(zé)從頁(yè)面獲得參數(shù)、把數(shù)據(jù)傳回頁(yè)面、控制頁(yè)面流傳和調(diào)用業(yè)務(wù)層的接口。持久層使用
hibernate
,在設(shè)計(jì)上我不是按
dao
方式為每個(gè)對(duì)象建立相應(yīng)的
dao
,也不是
ddd
推薦的每個(gè)
domain
一個(gè)
repository
,而是分成了
Persistence
和
Fetcher2
個(gè)接口。
Persistence
處理持久相關(guān)如
save
和
remove
方法,
Fetcher
則處理
get
相關(guān)。這樣分的原因也很簡(jiǎn)單,
persistence
是很穩(wěn)固的,對(duì)象都可以共用一個(gè)接口如
save
(
Object
),而
fetcher
就千變?nèi)f化,需要分頁(yè)、排序等接口。
這樣設(shè)計(jì)是與業(yè)務(wù)層架構(gòu)相關(guān)的。我采用的是
domain
對(duì)象(簡(jiǎn)稱
DO
)
+
一層薄薄
fa?ade
的方式。
DO
處理自身的邏輯,包括持久功能。本身
DO
是沒(méi)有持久能力的,需要依靠注入的
persistence
接口,這里就體現(xiàn)按
Persistence
和
Fetcher
分開(kāi)的一個(gè)好處,
persistence
所有
DO
可以共用一個(gè),給編程帶來(lái)了方便。
Openfans
中采用的是
DO
繼承一個(gè)抽象
PersistentObject
類的方式,這樣
DO
方便的獲得了注入的能力和持久的能力。這樣做有何優(yōu)缺點(diǎn)還需要做些討論,為了方便我也就先這么用。
PersistentObject
代碼如下:
public abstract class PersistentObject implements NeedPersist {
?????? private Persistence persistence;
?
?????? public void save() {
????????????? persistence.save(this);
?
?????? }
?
?????? public void remove() {
????????????? persistence.remove(this);
?????? }
?
?????? public void setPersistence(Persistence persistence) {
????????????? this.persistence = persistence;
?????? }
?
?????? public Persistence getPersistence() {
????????????? return persistence;
?????? }
}
這樣
DO
只需要注入
persistence
就獲得了持久的能力,而且可以把這種能力往下傳遞。
DO
獲得了持久能力,就有點(diǎn)接近富血模型的想法了,他能夠處理一些業(yè)務(wù),做持久然后調(diào)用引用對(duì)象的業(yè)務(wù)和持久方法
(
從另外的角度看持久與業(yè)務(wù)其實(shí)是分不開(kāi)的
)
。這樣把業(yè)務(wù)封裝在了領(lǐng)域本身,更適于用領(lǐng)域?qū)ο蟪霭l(fā)的方式去思考問(wèn)題。領(lǐng)域?qū)拥?/span>
fa?ade
主要是為了
Transaction
管理和隱藏
DO
接口。這樣
DO
的業(yè)務(wù)方法都可以設(shè)置成
friendly
,僅相互間可見(jiàn)。
Fa?ade
就放在
domain
包中,它負(fù)責(zé)給
DO
注入
persistence bean
,調(diào)用
DO
的接口,提供給
controller
一個(gè)
use case
級(jí)別的接口,同時(shí)它也代理
fetcher
的接口。
有了這個(gè)架構(gòu),實(shí)現(xiàn)起來(lái)也不復(fù)雜,要配置的
bean
很少(現(xiàn)在還沒(méi)有使用
spring 2.0
將
DO
配置在容器中)。設(shè)計(jì)就從
DO
出發(fā),明確它的屬性和方法,讓
hibernate
自己生成數(shù)據(jù)庫(kù)表。
??????
這樣設(shè)計(jì)也算是一次嘗試吧,其中肯定有很多考慮不周的地方,需要不斷的討論和改進(jìn)。