2006年5月29日
應(yīng)朋友邀請(qǐng),周六早從上海出發(fā)往杭州參加阿里巴巴網(wǎng)俠大會(huì)。同行四人,有銳道的macro chen、楊光(還是我?guī)煹埽⒁苿?dòng)的王偉旭(特長(zhǎng)是linux和網(wǎng)絡(luò)安全,也是中國(guó)linux推廣的先驅(qū))。一路上,言談甚歡。老莊給我們訂的票,他一早腸胃有恙,仍然堅(jiān)持把票送到火車站,之后去吊鹽水,下午又出現(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),還與部門、其它部門甚至整個(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è)教父,一直灌輸他們不要出賣同伙。
馬云就是這個(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í)間順便去廈門旅游。廈門依山傍海,的確是旅游的好去處。晚上到海邊,涼風(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á)到客戶可以接受為止。
由于不是專門的測(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
等,也都一知半解),選修了一門算法導(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ì)努力的。