James Gosling : Java之父
文/陶文
作為Java之父,James Gosling的名字可謂是耳熟能詳。當(dāng)人們?cè)u(píng)論一種編程語(yǔ)言時(shí),總喜歡捎帶著把下蛋的母雞一起帶上。Java做為中國(guó)的編程語(yǔ)言學(xué)習(xí)者餐桌上有限的那么幾樣餐點(diǎn)中的流行款式,自然是讓James Gosling風(fēng)光不已。雖然James Gosling現(xiàn)在已經(jīng)不是領(lǐng)導(dǎo)Java發(fā)展潮流的領(lǐng)軍人物了,做為Sun的開(kāi)發(fā)者產(chǎn)品組的CTO,怎么算來(lái)也是身居高位了,俗事纏身吧,但是這并不妨礙其對(duì)于Java一如既往的愛(ài)護(hù),表達(dá)著各式各樣鮮明的觀點(diǎn),引發(fā)一場(chǎng)又一場(chǎng)的爭(zhēng)論。
James Gosling是很愛(ài)Java的——是啊,哪有當(dāng)父母的不愛(ài)自己的孩子的呢。James Gosling也是很愛(ài)Sun的——是啊,哪有當(dāng)領(lǐng)導(dǎo)的不愛(ài)自己的公司的呢。于是我們?cè)谂u(píng).NET的安全性的隊(duì)伍前頭,在褒揚(yáng)Java性能的隊(duì)伍前頭,在抨擊SWT開(kāi)倒車(chē)的隊(duì)伍前頭,在給NetBeans大唱贊歌的隊(duì)伍前頭,我們都看到了James Gosling的身影。無(wú)論對(duì)錯(cuò)、偏見(jiàn)或者固執(zhí),至少說(shuō)明了Gosling的鮮明個(gè)性絲毫沒(méi)有受到年齡的影響。也許也只有這種天才而偏執(zhí)的人物才能創(chuàng)造出Java這般偉大的語(yǔ)言來(lái)吧。
Bill Joy : 軟件業(yè)的愛(ài)迪生
文/徐昊
Joy生于1954年,1982年與Vinod Khosla, Scott McNealy和Andy Bechtolsheim一起創(chuàng)建了Sun Microsystems,并從那時(shí)起擔(dān)任首席科學(xué)家,直到2003年離開(kāi)。他是一位令人崇敬的軟件天才,他在軟件和硬件的歷史上留下了無(wú)數(shù)令人仰止的傳奇。
在上個(gè)世紀(jì)80年代早期,DARPA與BBN達(dá)成協(xié)議,準(zhǔn)備將Vinton Cerf和Bob Kahn設(shè)計(jì)的TCP/IP協(xié)議添加到Berkeley UNIX中。Bill Joy被委派來(lái)完成這項(xiàng)任務(wù),然而他卻拒絕將BBN的TCP/IP協(xié)議棧添加到BSD中,因?yàn)樵谒难壑蠦BN的TCP/IP實(shí)現(xiàn)還遠(yuǎn)不夠好,于是他就寫(xiě)了一個(gè)高性能的TCP/IP協(xié)議棧。John Gage回憶道,“BBN和DARPA簽署了巨額合同來(lái)實(shí)現(xiàn)TCP/IP協(xié)議,然而他們的員工所編寫(xiě)的代碼遠(yuǎn)沒(méi)有一個(gè)研究生所做的好。于是他們邀請(qǐng)Bill Joy參加他們的一個(gè)會(huì)議,這位研究生穿著一件T-Shirt就出現(xiàn)了,他們?cè)儐?wèn)他,‘你是如何做到的呢?’Bill回答說(shuō),‘這是非常簡(jiǎn)單的一件事,你讀一下協(xié)議然后就可以編碼了’”。除了TCP/IP協(xié)議,基于分頁(yè)的虛擬內(nèi)存系統(tǒng)最早也是由Bill Joy添加到Berkeley UNIX內(nèi)核當(dāng)中的。同時(shí)他還是vi、csh、早期Pascal編譯器的作者。
關(guān)于Bill Joy驚人的軟件才能流傳最廣的一個(gè)傳奇是,據(jù)說(shuō)他在上研究生的時(shí)候,想看看自己能不能寫(xiě)一個(gè)操作系統(tǒng)出來(lái),于是就在三天里寫(xiě)了一個(gè)非常簡(jiǎn)陋,但是可以使用的Unix系統(tǒng), 傳說(shuō)就是BSD的前身。雖然如此夸張的才情令人難以置信,但是考慮到主角是Bill Joy,還是有一定的可信度的。Bill Joy碩士畢業(yè)之后,決定到工業(yè)界發(fā)展,于是就到了當(dāng)時(shí)只有一間辦公室的Sun, 他作為主要設(shè)計(jì)者參與了SPARC微處理器的設(shè)計(jì),負(fù)責(zé)設(shè)計(jì)最為關(guān)鍵的一部分電路。這樣兼精軟硬件的天才實(shí)在是讓人不得不佩服啊。1995年,Sun發(fā)布了轟動(dòng)世界的Java語(yǔ)言。當(dāng)然,Bill Joy對(duì)Java也作出了不少的貢獻(xiàn),首先是JINI——一種針對(duì)分布式服務(wù)的基礎(chǔ)連接技術(shù)。任何可以?xún)?nèi)嵌JVM的電子設(shè)備都可以通過(guò)JINI相互連接;JXTA是基于Java的P2P協(xié)議,允許互聯(lián)網(wǎng)上的軟件進(jìn)行點(diǎn)對(duì)點(diǎn)交流和協(xié)作。
這個(gè)其貌不揚(yáng)的瘦高個(gè),有著凌亂的亞麻色頭發(fā),被《財(cái)富》雜志譽(yù)為“網(wǎng)絡(luò)時(shí)代的愛(ài)迪生”的技術(shù)狂人,在短短的二十年間,創(chuàng)造了無(wú)數(shù)令人心動(dòng)的軟件。在MIT的BBS上曾有一個(gè)帖子,說(shuō)微軟電話面試有一道題,問(wèn)“Who do you think is the best coder, and why?”雖然回復(fù)的帖子中大家都聲明列舉的best coder排名不分先后,然而大多數(shù)人仍把Bill Joy列在第一位,或許可以從一個(gè)側(cè)面驗(yàn)證Bill Joy在廣大Programmer心目中的地位吧。
Joshua Bloch : Java 2 元?jiǎng)?br />
早在1996年,適逢Java剛剛嶄露頭角,年內(nèi)好事連連。先是1月份發(fā)布JDK 1.0,然后是5月底在舊金山召開(kāi)首屆JavaOne大會(huì),年末又是JDK 1.1緊跟其后。正是在Java技術(shù)如火如荼、大展拳腳的背景之下,Joshua Bloch來(lái)到了Sun,開(kāi)始了他帶領(lǐng)Java社區(qū)步入“迦南美地”的漫長(zhǎng)歷程。
很快,他被從安全組調(diào)入核心平臺(tái)組,從事底層API設(shè)計(jì)。至此以后,每逢JDK的重大版本發(fā)布,總能在其中見(jiàn)到Joshua的“妙筆”。JDK 1.1中的java.math、1.4中的assertions,還有大家所熟識(shí)的Collections Framework皆是Joshua一手打造。其中的Collections Framework還獲得了當(dāng)年的Jolt大獎(jiǎng)。到了J2SE 5.0研發(fā)階段,身為平臺(tái)組構(gòu)架師的Joshua接掌了Tiger大旗,其核心地位已然無(wú)人可以替代。作為T(mén)iger的代言人和領(lǐng)路人,沒(méi)有誰(shuí)比Joshua更清楚Tiger。相信大家一定還記得Joshua當(dāng)年仿效英國(guó)詩(shī)人William Blake所做的詠Tiger詩(shī)八首,優(yōu)雅的筆調(diào),透出大師深厚底蘊(yùn)的同時(shí),也道出了Tiger的幾大重要特性,這些特性是自JDK 1.1引入Inner Class以來(lái),Java最大的語(yǔ)法改進(jìn)。
Java風(fēng)雨十年,從JDK 1.1到J2SE 5.0,Joshua實(shí)在功不可沒(méi)。難怪有人戲言,假如將James Gosling比作Java之父,那么Joshua就是一手將Java “哺育”成人的Java之母。Joshua對(duì)Java的貢獻(xiàn)還不止于JDK,提起他的大作《Effective Java》(Addison Wesley, 2001),相信Java粉絲們一定耳熟能詳。該書(shū)榮膺2002年度Jolt大獎(jiǎng),且備受James Gosling推崇。書(shū)中57條頗具實(shí)用價(jià)值的經(jīng)驗(yàn)規(guī)則,來(lái)自Joshua多年來(lái)在JDK開(kāi)發(fā)工作中,尤其是Collections Framework設(shè)計(jì)中的實(shí)踐心得,各個(gè)有理有據(jù),剖析深入,也足見(jiàn)其深厚功力。該書(shū)對(duì)Java社群的影響,猶如C++社群中的《Effective C++》。Joshua對(duì)JCP的貢獻(xiàn)也不小。他是JSR201和JSR175的領(lǐng)導(dǎo)者,前者包含了Tiger四大語(yǔ)言特性,后者則為Java提供了元數(shù)據(jù)支持。此外,他還是JSR166的發(fā)起人之一(該JSR由Doug Lea領(lǐng)導(dǎo)),并且是許多其他JSR的參與者。Joshua目前是JCP為數(shù)不多的幾個(gè)執(zhí)行委員會(huì)成員之一。
Joshua Bloch給人的印象是謙遜平和,行事低調(diào)而不喜拋頭露面,一個(gè)典型的技術(shù)人員和實(shí)干家。不過(guò)即便如此,也絲毫不會(huì)減弱他對(duì)Java技術(shù)的卓越貢獻(xiàn)和對(duì)Java社區(qū)的絕對(duì)影響力。有人說(shuō),如果他能更彰顯一些,就很有可能成為Java開(kāi)發(fā)者中的領(lǐng)軍人物,就有如Don Box之于微軟社群。
2004年7月初,就在Tiger發(fā)布在即之時(shí),就在Jusha Bloch剛剛榮獲Sun“杰出工程師(Distinguished Engineer)”的稱(chēng)號(hào)之時(shí),他突然離開(kāi)Sun而去了正值發(fā)展態(tài)勢(shì)迅猛的Google。當(dāng)他離開(kāi)Sun的消息在TSS發(fā)布之后,眾多擁躉表達(dá)了懷念與不舍之情。一年過(guò)去了,我們還沒(méi)有獲知Joshua的任何近聞,似乎又是他行事低調(diào)的一貫作風(fēng)所致,不知他在Google狀況如何。希望Joshua依然能繼續(xù)“摩西未盡的事業(yè)”,以他的影響力推動(dòng)Java社群繼續(xù)前行。據(jù)稱(chēng),《Effective Java》的下一版會(huì)加入Java 5.0的部分,讓我們翹首以待吧。
Bruce Eckel : 功勛卓著的機(jī)會(huì)主義分子
Bruce Eckel原本是一位普通的匯編程序員。不知道是什么因緣際會(huì),他轉(zhuǎn)行去寫(xiě)計(jì)算機(jī)技術(shù)圖書(shū),卻在此大紅大紫。他成功的秘訣不外乎兩點(diǎn):超人的表達(dá)能力和捕捉機(jī)會(huì)的能力。他最早的一本書(shū)是1990年代初期的《C++ Inside & Out》,隨后,在1995年他寫(xiě)出了改變自己命運(yùn)的《Thinking in C++》。如果說(shuō)這本書(shū)充分表現(xiàn)了他作為優(yōu)秀技術(shù)作家的一面,那么隨后他寫(xiě)作《Thinking in Java》并因此步入頂級(jí)技術(shù)作家行列,則體現(xiàn)了他作為優(yōu)秀的機(jī)會(huì)主義分子善于捕捉機(jī)會(huì)的另一面。寫(xiě)作中擅長(zhǎng)舉淺顯直接的小例子來(lái)說(shuō)明問(wèn)題,語(yǔ)言生動(dòng),娓娓道來(lái),特別適合于缺乏實(shí)踐經(jīng)驗(yàn)的初學(xué)者。因此《Thinking in Java》儼然成為天字第一號(hào)的Java教科書(shū),對(duì)Java的普及與發(fā)展發(fā)揮著不可忽略的作用。不過(guò)公允地說(shuō),Bruce Eckel的書(shū)欠深刻。比如在“Thinking in…”系列中對(duì)設(shè)計(jì)模式的解說(shuō)就有失大師水準(zhǔn)。這一方面是因?yàn)闀?shū)的定位非常清晰,另一方面也是因?yàn)锽ruce太過(guò)分心趕潮流,未能深入之故。TIJ之后,他預(yù)言Python將火,就匆匆跑去寫(xiě)了半本《Thinking in Python》。后來(lái)Python并未如期而旺,于是他也就把書(shū)稿撂在那里不過(guò)問(wèn)了,機(jī)會(huì)主義的一面暴露無(wú)遺。我們也可以善意的猜測(cè)一下,他的下一個(gè)投機(jī)對(duì)象會(huì)是什么呢?Ruby?.NET?MDA?總之,是什么我都不奇怪。
Rickard Oberg :J2EE奇才
Oberg的作品很多,流行的代碼生成工具XDoclet和MVC框架WebWork都出自他的手筆。這兩個(gè)框架有一個(gè)共同的特點(diǎn),即它們的功能雖然簡(jiǎn)單,但設(shè)計(jì)都非常優(yōu)雅靈活,能夠很方便地?cái)U(kuò)展新功能甚至移植到新環(huán)境下使用。優(yōu)雅的設(shè)計(jì)源自O(shè)berg的過(guò)人才華,簡(jiǎn)單的功能則折射出他玩世不恭的人生態(tài)度。正是這兩種特質(zhì)的融合,才造就了這個(gè)不世出的奇才。
1999年,JDK 1.3發(fā)布,其中帶來(lái)了一個(gè)重要的新特性:動(dòng)態(tài)代理(Dynamic Proxy)。當(dāng)所有人都還在對(duì)這項(xiàng)新技術(shù)的用途感到迷惑時(shí),Oberg發(fā)現(xiàn)用它便可以輕松攻克EJB容器實(shí)現(xiàn)中的一些難關(guān)。這一發(fā)現(xiàn)的產(chǎn)物就是一本《Mastering RMI》,以及大名鼎鼎的JBoss應(yīng)用服務(wù)器。但Oberg很快又讓世人見(jiàn)識(shí)了他的玩世不恭。由于和總經(jīng)理Marc Fleury在經(jīng)營(yíng)理念上不合,Oberg抱怨“法國(guó)的天空總讓我感到壓抑”,甩手離開(kāi)了自己一手打造的JBoss。此后的幾年里,他和老友Hani Suleiman不斷地對(duì)JBoss的“專(zhuān)業(yè)開(kāi)源”模式和Marc Fleury的商人味道冷嘲熱諷,讓眾人為他的孩子氣扼腕嘆息。
2002年10月,微軟推出Petstore示例應(yīng)用的.NET版本,并宣稱(chēng)其性能比Java Petstore高出數(shù)倍。正是Oberg深入分析這個(gè)示例應(yīng)用的源代碼,在第一時(shí)間指出它大量運(yùn)用了SQL Server專(zhuān)有的特性,性能對(duì)比根本不具參考價(jià)值。后來(lái)Oberg又先后關(guān)注了AOP和IoC容器,兩者都成為了J2EE架構(gòu)的新寵。
Doug Lea : 世界上對(duì)Java影響力最大的個(gè)人
文/KIT
如果IT的歷史,是以人為主體串接起來(lái)的話,那么肯定少不了Doug Lea。這個(gè)鼻梁掛著眼鏡,留著德王威廉二世的胡子,臉上永遠(yuǎn)掛著謙遜靦腆笑容,服務(wù)于紐約州立大學(xué)Oswego分校計(jì)算器科學(xué)系的老大爺。
說(shuō)他是這個(gè)世界上對(duì)Java影響力最大的個(gè)人,一點(diǎn)也不為過(guò)。因?yàn)閮纱蜫ava歷史上的大變革,他都間接或直接的扮演了舉足輕重的腳色。一次是由JDK 1.1到JDK 1.2,JDK1.2很重要的一項(xiàng)新創(chuàng)舉就是Collections,其Collection的概念可以說(shuō)承襲自Doug Lea于1995年發(fā)布的第一個(gè)被廣泛應(yīng)用的collections;一次是2004年所推出的Tiger。Tiger廣納了15項(xiàng)JSRs(Java Specification Requests)的語(yǔ)法及標(biāo)準(zhǔn),其中一項(xiàng)便是JSR-166。JSR-166是來(lái)自于Doug編寫(xiě)的util.concurrent包。
值得一提的是: Doug Lea也是JCP (Java小區(qū)項(xiàng)目)中的一員。
Doug是一個(gè)無(wú)私的人,他深知分享知識(shí)和分享蘋(píng)果是不一樣的,蘋(píng)果會(huì)越分越少,而自己的知識(shí)并不會(huì)因?yàn)榻o了別人就減少了,知識(shí)的分享更能激蕩出不一樣的火花。《Effective JAVA》這本Java經(jīng)典之作的作者Joshua Blosh便在書(shū)中特別感謝Doug是此書(shū)中許多構(gòu)想的共鳴板,感謝Doug大方分享豐富而又寶貴的知識(shí)。這位并發(fā)編程的大師級(jí)人物的下一步,將會(huì)帶給Java怎樣的沖擊,不禁令人屏息以待。
Scott McNealy :SUN十年來(lái)的掌舵者
文/KIT
McNealy,Sun的CEO、總裁兼董事長(zhǎng)。他曾經(jīng)狂傲的說(shuō):“摧毀微軟是我們每個(gè)人的任務(wù)。”這位英勇的硅谷英雄,似乎帶頭起義,試圖組織一個(gè)反微軟陣線聯(lián)盟,以對(duì)抗微軟這股龐大的托拉斯惡勢(shì)力。他時(shí)常口出驚人之語(yǔ),在公開(kāi)場(chǎng)合大肆的批評(píng)微軟,并曾經(jīng)說(shuō)微軟的.NET是.NOT。
Scott McNealy先后畢業(yè)于哈佛大學(xué)及史丹佛大學(xué),分別持有經(jīng)濟(jì)學(xué)學(xué)士學(xué)位及企管碩士。1982年MBA畢業(yè)的他和三個(gè)同學(xué)共同合伙創(chuàng)建了Sun,并于1984年成為Sun的執(zhí)行官。“要么吞了別人,不然就被別人吞了”是Scott McNealy的名言錄之一。他擅長(zhǎng)以信念帶動(dòng)員工,鼓舞士氣。極富自信的他,對(duì)于認(rèn)定的事,總是堅(jiān)持自己的想法,因此有人形容他是一個(gè)剛愎自用的決策者。
身為Sun這艘船的掌舵者,Scott McNealy能夠看多遠(yuǎn),Sun就能走多遠(yuǎn)。Scott McNealy認(rèn)為將來(lái)軟件界是一個(gè)只有服務(wù),沒(méi)有產(chǎn)品的世代。他希望打造出Sun不是一個(gè)純靠硬件賺錢(qián)的公司。從Open Source到Open Solaris,Sun希望可以成為提供整合性解決方案的服務(wù)廠商。Solaris 10 + UltraSPARC是否可以像Scott McNealy希望的是下一匹世紀(jì)黑馬呢?Sun是否能以股價(jià)來(lái)證明華爾街分析師及普羅大眾的誹短流長(zhǎng)?Scott McNealy是否能帶領(lǐng)著Sun成為繼微軟之后的下一個(gè)巨人,一場(chǎng)場(chǎng)IT界的爭(zhēng)霸戰(zhàn)值得我們拭目以待。
Rod Johnson : 用一本書(shū)改變了Java世界的人
Rod在悉尼大學(xué)不僅獲得了計(jì)算機(jī)學(xué)位,同時(shí)還獲得了音樂(lè)學(xué)位。更令人吃驚的是在回到軟件開(kāi)發(fā)領(lǐng)域之前,他還獲得了音樂(lè)學(xué)的博士學(xué)位。有著相當(dāng)豐富的C/C++技術(shù)背景的Rod早在1996年就開(kāi)始了對(duì)Java服務(wù)器端技術(shù)的研究。他是一個(gè)在保險(xiǎn)、電子商務(wù)和金融行業(yè)有著豐富經(jīng)驗(yàn)的技術(shù)顧問(wèn),同時(shí)也是JSR-154(Servlet 2.4)和JDO 2.0的規(guī)范專(zhuān)家、JCP的積極成員。
真正引起了人們的注意的,是在2002年Rod Johnson根據(jù)多年經(jīng)驗(yàn)撰寫(xiě)的《Expert One-on-One J2EE Design and Development》。其中對(duì)正統(tǒng)J2EE架構(gòu)的臃腫、低效的質(zhì)疑,引發(fā)了人們對(duì)正統(tǒng)J2EE的反思。這本書(shū)也體現(xiàn)了Rod Johnson對(duì)技術(shù)的態(tài)度,技術(shù)的選擇應(yīng)該基于實(shí)證或是自身的經(jīng)驗(yàn),而不是任何形式的偶像崇拜或者門(mén)戶(hù)之見(jiàn)。正是這本書(shū)真正地改變了Java世界。基于這本書(shū)的代碼,Rod Johnson創(chuàng)建了輕量級(jí)的容器Spring。Spring的出現(xiàn),使得正統(tǒng)J2EE架構(gòu)一統(tǒng)天下的局面被打破。基于Struts+Hibernate+Spring的J2EE架構(gòu)也逐漸得到人們的認(rèn)可,甚至在大型的項(xiàng)目架構(gòu)中也逐漸開(kāi)始應(yīng)用。
Rod Johnson的新作《Expert One-on-one J2EE Development without JEB》則更讓人吃驚,單單“Without EJB”一詞就會(huì)讓大多數(shù)J2EE架構(gòu)師大跌眼鏡了。不過(guò)Rod Johnson可能僅僅是想通過(guò)“Without EJB”一詞表明應(yīng)該放開(kāi)門(mén)戶(hù)之見(jiàn)。這也是Rod Johnson一貫的作風(fēng),。也許正是這種思想,促使得Rod Johnson創(chuàng)建了Spring,真正改變了Java世界。
Alan Kay :Java的精神先鋒
Sun的官方Java教材中有一句話,說(shuō)Java是“C++的語(yǔ)法與Smalltalk語(yǔ)義的結(jié)合”。而Smalltalk的創(chuàng)造者就是Alan Kay。
Alan Kay于1970年加入Xerox公司的Palo Alto研究中心。早在70年代初期,Alan Kay等人開(kāi)發(fā)了世界上第二個(gè)面向?qū)ο笳Z(yǔ)言Smalltalk,因此,Alan Kay被譽(yù)為Smalltalk之父。2003年,Alan Key因?yàn)樵诿嫦驅(qū)ο蟪绦蛟O(shè)計(jì)上的杰出貢獻(xiàn),獲得了有計(jì)算機(jī)界的諾貝爾獎(jiǎng)之稱(chēng)的ACM Turing Award。
Alan Kay成名于Smapltalk和OOP,而Java雖然在語(yǔ)言上類(lèi)似于C,但是在語(yǔ)義上非常接近Smalltalk,很多Java中的設(shè)計(jì)思想在Alan Kay的文獻(xiàn)中找到根源,也有些人將Alan Kay尊為Java思想的先驅(qū)。不過(guò)遺憾的是似乎Alan Kay老先生對(duì)Java并不買(mǎi)賬,反倒攻擊說(shuō)Java是存在致命缺陷的編程語(yǔ)言,Java的成功不是由于Java本身的內(nèi)在價(jià)值,而是其商業(yè)化的成功。Alan Kay欣賞的是Lisp,他認(rèn)為L(zhǎng)isp是軟件的麥克斯韋方程,其中的許多想法是軟件工程和計(jì)算機(jī)科學(xué)的一部分。看來(lái)?yè)碛蠥lan Kay這樣一位重量級(jí)的Java先驅(qū)仍是我們Java一廂情愿的單戀吧。
Kent Beck : 領(lǐng)導(dǎo)的敏捷潮
Beck全家似乎都彌漫著技術(shù)的味道。生長(zhǎng)在硅谷, 有著一個(gè)對(duì)無(wú)線電癡迷的祖父,以及一個(gè)電器工程師父親。從小就引導(dǎo)Kent Beck成為了業(yè)余無(wú)線電愛(ài)好者。
在俄勒岡州大學(xué)讀本科期間,Kent Beck就開(kāi)始研究起模式。然而在他最終拿到計(jì)算機(jī)學(xué)位之前,他卻是在計(jì)算機(jī)和音樂(lè)中交替學(xué)習(xí)。似乎Java大師都能夠有這樣的能耐,另一Java大牛Rod Johnson同樣也擁有音樂(lè)學(xué)的博士學(xué)位。
Kent Beck一直倡導(dǎo)軟件開(kāi)發(fā)的模式定義。早在1993年,他就和Grady Booch(UML之父)發(fā)起了一個(gè)團(tuán)隊(duì)進(jìn)行這個(gè)方面的研究。雖然著有了《Smalltalk Best Practice Patterns》一書(shū),但這可能并不是Kent Beck最大的貢獻(xiàn)。他于1996年在DaimlerChrysler啟動(dòng)的關(guān)于軟件開(kāi)發(fā)的項(xiàng)目,才真正地影響后來(lái)的軟件開(kāi)發(fā)。這次的杰作就是XP(極限編程)的方法學(xué)。
和軟件開(kāi)發(fā)大師Martin Fowler合著的《Planning Extreme Programming》可謂是關(guān)于XP的奠基之作。從此,一系列的作品如《Test Driven Development: By Example》,《Extreme Programming Explained: Embrace Change》讓更多的人領(lǐng)略到了極限編程的精髓,也逐步導(dǎo)致了極限編程的流行。
Kent Beck的貢獻(xiàn)遠(yuǎn)不僅如此。對(duì)于眾多的Java程序員來(lái)說(shuō),他和Erich Gamma共同打造的JUnit,意義更加重大。也許正式這個(gè)簡(jiǎn)單而又強(qiáng)大的工具,讓眾多的程序員更加認(rèn)可和信賴(lài)極限編程,從而引起了Java敏捷開(kāi)發(fā)的狂潮吧。
|
轉(zhuǎn)載人員:Nicholas
功能:在線拍照
簡(jiǎn)介:用flex與java結(jié)合實(shí)現(xiàn)在線拍照
需求:為了滿(mǎn)足希望通過(guò)攝像頭拍照的圖片,然后通過(guò)服務(wù)器來(lái)展示需要
效果:
后臺(tái):
前臺(tái):

實(shí)現(xiàn)代碼:
flex:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="388" height="222" creationComplete="initApp()" backgroundColor="#A6C9E2">
<mx:Style>
Alert{font-size:12px;}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.events.CloseEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.core.Application;
private static const DEFAULT_CAMERA_WIDTH:Number = 160; //攝像頭顯示寬度
private static const DEFAULT_CAMERA_HEIGHT:Number = 120; //攝像頭顯示高度
private var DEFAULT_WEBSERVICE_URL:String = ""; //WebService地址
private var str:String;
private var m_camera:Camera; //定義一個(gè)攝像頭
private var m_localVideo:Video; //定義一個(gè)本地視頻
private var m_pictureBitmapData:BitmapData //定義視頻截圖
[Bindable]
private var m_pictureData:String;
private function initApp():void
{
t_btn_Shooting.enabled = false;
t_ban_Save.enabled = false;
initCamera();
DEFAULT_WEBSERVICE_URL = Application.application.parameters.contextPath+"/onLineTakePhotoServlet";
t_ws_SavePicture.url=DEFAULT_WEBSERVICE_URL;
}
//初始化攝像頭
private function initCamera():void
{
m_camera = Camera.getCamera();
if(m_camera != null)
{
m_camera.addEventListener(StatusEvent.STATUS,__onCameraStatusHandler);
m_camera.setMode(DEFAULT_CAMERA_WIDTH,DEFAULT_CAMERA_HEIGHT,30);
m_localVideo = new Video();
m_localVideo.width = DEFAULT_CAMERA_WIDTH;
m_localVideo.height = DEFAULT_CAMERA_HEIGHT;
m_localVideo.attachCamera(m_camera);
t_vd_Video.addChild(m_localVideo);
}
else
{
Alert.show("沒(méi)有找到攝像頭,是否重新查找。","提示:",Alert.OK|Alert.NO,this,__InitCamera);
return;
}
}
//拍照按鈕事件,進(jìn)行視頻截圖
private function SnapshotPicture():void
{
m_pictureBitmapData = new BitmapData(DEFAULT_CAMERA_WIDTH,DEFAULT_CAMERA_HEIGHT);
m_pictureBitmapData.draw(t_vd_Video,new Matrix());
var m_pictureBitmap:Bitmap = new Bitmap(m_pictureBitmapData);
t_img_Picture.addChild(m_pictureBitmap);
t_panel_Picture.visible = true;
t_ban_Save.enabled = true;
}
//保存按鈕事件,保存視頻截圖
//通過(guò)WebService保存
private function SavePicture():void
{
m_pictureData = "";
for(var i:int = 0; i < DEFAULT_CAMERA_WIDTH; i++)
{
for(var j:int = 0; j < DEFAULT_CAMERA_HEIGHT; j++)
{
if(m_pictureData.length > 0)
{
m_pictureData += "," + m_pictureBitmapData.getPixel32(i,j).toString();
}
else
{
m_pictureData = m_pictureBitmapData.getPixel32(i,j).toString();
}
}
}
var params:URLVariables = new URLVariables();
params.width = DEFAULT_CAMERA_WIDTH;
params.height = DEFAULT_CAMERA_HEIGHT;
params.bitmap_data = m_pictureData;
t_ws_SavePicture.send(params);
}
//檢測(cè)攝像頭權(quán)限事件
private function __onCameraStatusHandler(event:StatusEvent):void
{
if(!m_camera.muted)
{
t_btn_Shooting.enabled = true;
}
else
{
Alert.show("無(wú)法鏈接到活動(dòng)攝像頭,是否重新檢測(cè)。","提示:",Alert.OK|Alert.NO,this,__InitCamera);
}
m_camera.removeEventListener(StatusEvent.STATUS,__onCameraStatusHandler);
}
//當(dāng)攝像頭不存在,或連接不正常時(shí)重新獲取
private function __InitCamera(event:CloseEvent):void
{
if(event.detail == Alert.OK)
{
initApp();
}
}
//WebService保存圖片成功事件
private function __onSavePictureResult(event:ResultEvent):void
{
//trace(event.result);
if(event.result.toString() != "保存失敗")
{
str = event.result.toString();
Alert.show("保存成功,是否關(guān)閉窗口?","提示",3,this,__onAlertCloseHandler);
}
else
{
Alert.show(event.result.toString(),"提示",Alert.OK);
}
}
//連接WebService失敗事件
private function __onSavePictureFault(event:FaultEvent):void
{
//Alert.show(event.fault.toString(),"提示",Alert.OK);
Alert.show("連接服務(wù)器失敗。","提示",Alert.OK);
}
//保存圖片成功后的彈出窗口確認(rèn)事件
private function __onAlertCloseHandler(event:CloseEvent):void
{
if(event.detail == Alert.YES)
{
ExternalInterface.call("setValueToField",str);
}
}
]]>
</mx:Script>
<mx:HTTPService id="t_ws_SavePicture" showBusyCursor="true" method="POST" useProxy="false" result="__onSavePictureResult(event)" fault="__onSavePictureFault(event)"/>
<mx:Panel x="10" y="10" width="180" height="200" layout="absolute" title="視頻拍照" fontSize="12">
<mx:VideoDisplay id="t_vd_Video" width="160" height="120"/>
<mx:ControlBar horizontalAlign="right">
<mx:Button id="t_btn_Shooting" label="拍照" click="SnapshotPicture()"/>
</mx:ControlBar>
</mx:Panel>
<mx:Panel id="t_panel_Picture" x="198" y="10" width="180" height="200" layout="absolute" title="拍照?qǐng)D片" fontSize="12" visible="false">
<mx:Image id="t_img_Picture" x="0" y="0" width="160" height="120"/>
<mx:ControlBar horizontalAlign="right">
<mx:Button id="t_ban_Save" label="保存" click="SavePicture()" />
</mx:ControlBar>
</mx:Panel>
</mx:Application>
java:
package cn.myapps.core.onlinetakephoto;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.myapps.constans.Environment;
import cn.myapps.util.sequence.Sequence;
/**
* Servlet implementation class onLineTakePhotoServlet
*/
public class onLineTakePhotoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Environment env = Environment.getInstance();
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
String bitmap_data = request.getParameter("bitmap_data");
int width = Integer.parseInt(request.getParameter("width"));
int height = Integer.parseInt(request.getParameter("height"));
BufferedImage img = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
try {
int w = width;
int h = height;
int[] pixels = new int[w * h];
String[] m_tempPics = bitmap_data.split(",");
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
Long pic_argb = Long.parseLong(m_tempPics[x * h + y]);
int a = (int) (pic_argb >> 24 & 0xFF);
int r = (int) (pic_argb >> 16 & 0xFF);
int g = (int) (pic_argb >> 8 & 0xFF);
int b = (int) (pic_argb & 0xFF);
pixels[y * w + x] = new Color(r, g, b, a).getRGB();
}
}
img.setRGB(0, 0, w, h, pixels, 0, w);
img.flush();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", bao);
byte[] data = bao.toByteArray();
String filePath = env.getRealPath("/uploads/photo");
//判斷路徑是否存在,若不存在則創(chuàng)建路徑
File upDir = new File(filePath);
if (!upDir.exists())
{
upDir.mkdir();
}
//生成隨機(jī)文件名
String saveName = Sequence.getSequence();
String fileName = saveName + ".jpg";
//寫(xiě)圖片
File f = new File(filePath+"\\" + fileName);
DataOutputStream dos = new DataOutputStream(new FileOutputStream(f));
dos.write(data);
dos.flush();
dos.close();
response.setContentType("text/xml");
response.getWriter().write("/uploads/photo/" + fileName);
}
catch(Exception ex)
{
response.setContentType("text/xml");
response.getWriter().write("保存失敗");
}
}
}
源碼:/Files/obpm/onlinetakephoto.rar
原創(chuàng)人員:Denny
LDAP快速入門(mén)
1. LDAP簡(jiǎn)介
LDAP(輕量級(jí)目錄訪問(wèn)協(xié)議,Lightweight Directory Access Protocol)是實(shí)現(xiàn)提供被稱(chēng)為目錄服務(wù)的信息服務(wù)。目錄服務(wù)是一種特殊的數(shù)據(jù)庫(kù)系統(tǒng),其專(zhuān)門(mén)針對(duì)讀取,瀏覽和搜索操作進(jìn)行了特定的優(yōu)化。目錄一般用來(lái)包含描述性的,基于屬性的信息并支持精細(xì)復(fù)雜的過(guò)濾能力。目錄一般不支持通用數(shù)據(jù)庫(kù)針對(duì)大量更新操作操作需要的復(fù)雜的事務(wù)管理或回卷策略。而目錄服務(wù)的更新則一般都非常簡(jiǎn)單。這種目錄可以存儲(chǔ)包括個(gè)人信息、web鏈結(jié)、jpeg圖像等各種信息。為了訪問(wèn)存儲(chǔ)在目錄中的信息,就需要使用運(yùn)行在TCP/IP 之上的訪問(wèn)協(xié)議—LDAP。
LDAP目錄中的信息是是按照樹(shù)型結(jié)構(gòu)組織,具體信息存儲(chǔ)在條目(entry)的數(shù)據(jù)結(jié)構(gòu)中。條目相當(dāng)于關(guān)系數(shù)據(jù)庫(kù)中表的記錄;條目是具有區(qū)別名DN (Distinguished Name)的屬性(Attribute),DN是用來(lái)引用條目的,DN相當(dāng)于關(guān)系數(shù)據(jù)庫(kù)表中的關(guān)鍵字(Primary Key)。屬性由類(lèi)型(Type)和一個(gè)或多個(gè)值(Values)組成,相當(dāng)于關(guān)系數(shù)據(jù)庫(kù)中的字段(Field)由字段名和數(shù)據(jù)類(lèi)型組成,只是為了方便檢索的需要,LDAP中的Type可以有多個(gè)Value,而不是關(guān)系數(shù)據(jù)庫(kù)中為降低數(shù)據(jù)的冗余性要求實(shí)現(xiàn)的各個(gè)域必須是不相關(guān)的。LDAP中條目的組織一般按照地理位置和組織關(guān)系進(jìn)行組織,非常的直觀。LDAP把數(shù)據(jù)存放在文件中,為提高效率可以使用基于索引的文件數(shù)據(jù)庫(kù),而不是關(guān)系數(shù)據(jù)庫(kù)。類(lèi)型的一個(gè)例子就是mail,其值將是一個(gè)電子郵件地址。
LDAP的信息是以樹(shù)型結(jié)構(gòu)存儲(chǔ)的,在樹(shù)根一般定義國(guó)家(c=CN)或域名(dc=com),在其下則往往定義一個(gè)或多個(gè)組織 (organization)(o=Acme)或組織單元(organizational units) (ou=People)。一個(gè)組織單元可能包含諸如所有雇員、大樓內(nèi)的所有打印機(jī)等信息。此外,LDAP支持對(duì)條目能夠和必須支持哪些屬性進(jìn)行控制,這是有一個(gè)特殊的稱(chēng)為對(duì)象類(lèi)別(objectClass)的屬性來(lái)實(shí)現(xiàn)的。該屬性的值決定了該條目必須遵循的一些規(guī)則,其規(guī)定了該條目能夠及至少應(yīng)該包含哪些屬性。例如:inetorgPerson對(duì)象類(lèi)需要支持sn(surname)和cn(common name)屬性,但也可以包含可選的如郵件,電話號(hào)碼等屬性。
2. LDAP簡(jiǎn)稱(chēng)對(duì)應(yīng)
- o– organization(組織-公司)
- ou – organization unit(組織單元-部門(mén))
- c - countryName(國(guó)家)
- dc - domainComponent(域名)
- sn – suer name(真實(shí)名稱(chēng))
- cn - common name(常用名稱(chēng))
3. 目錄設(shè)計(jì)
設(shè)計(jì)目錄結(jié)構(gòu)是LDAP最重要的方面之一。下面我們將通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明如何設(shè)計(jì)合理的目錄結(jié)構(gòu)。該例子將通過(guò)Netscape地址薄來(lái)訪文。假設(shè)有一個(gè)位于美國(guó)US(c=US)而且跨越多個(gè)州的名為Acme(o=Acme)的公司。Acme希望為所有的雇員實(shí)現(xiàn)一個(gè)小型的地址薄服務(wù)器。
我們從一個(gè)簡(jiǎn)單的組織DN開(kāi)始:
dn: o=Acme, c=US
Acme所有的組織分類(lèi)和屬性將存儲(chǔ)在該DN之下,這個(gè)DN在該存儲(chǔ)在該服務(wù)器的目錄是唯一的。Acme希望將其雇員的信息分為兩類(lèi):管理者(ou= Managers)和普通雇員(ou=Employees),這種分類(lèi)產(chǎn)生的相對(duì)區(qū)別名(RDN,relative distinguished names。表示相對(duì)于頂點(diǎn)DN)就shi :
dn: ou=Managers, o=Acme, c=US
dn: ou=Employees, o=Acme, c=US
在下面我們將會(huì)看到分層結(jié)構(gòu)的組成:頂點(diǎn)是US的Acme,下面是管理者組織單元和雇員組織單元。因此包括Managers和Employees的DN組成為:
dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US
dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US
dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US
為了引用Jason H. Smith的通用名(common name )條目,LDAP將采用cn=Jason H. Smith的RDN。然后將前面的父條目結(jié)合在一起就形成如下的樹(shù)型結(jié)構(gòu):
cn=Jason H. Smith
+ ou=Managers
+ o=Acme
+ c=US
-> dn: cn=Jason H. Smith,ou=Managers,o=Acme,c=US
現(xiàn)在已經(jīng)定義好了目錄結(jié)構(gòu),下一步就需要導(dǎo)入目錄信息數(shù)據(jù)。目錄信息數(shù)據(jù)將被存放在LDIF文件中,其是導(dǎo)入目錄信息數(shù)據(jù)的默認(rèn)存放文件。用戶(hù)可以方便的編寫(xiě)Perl腳本來(lái)從例如/etc/passwd、NIS等系統(tǒng)文件中自動(dòng)創(chuàng)建LDIF文件。
下面的實(shí)例保存目錄信息數(shù)據(jù)為testdate.ldif文件,該文件的格式說(shuō)明將可以在man ldif中得到。
在添加任何組織單元以前,必須首先定義Acme DN:
dn: o=Acme, c=US
objectClass: organization
這里o屬性是必須的
o: Acme
下面是管理組單元的DN,在添加任何管理者信息以前,必須先定義該條目。
dn: ou=Managers, o=Acme, c=US
objectClass: organizationalUnit
這里ou屬性是必須的。
ou: Managers
第一個(gè)管理者DN:
dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US
objectClass: inetOrgPerson
cn和sn都是必須的屬性:
cn: Jason H. Smith
sn: Smith
但是還可以定義一些可選的屬性:
telephoneNumber: 111-222-9999
mail: headhauncho@acme.com
localityName: Houston
可以定義另外一個(gè)組織單元:
dn: ou=Employees, o=Acme, c=US
objectClass: organizationalUnit
ou: Employees
并添加雇員信息如下:
dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US
objectClass: inetOrgPerson
cn: Ray D. Jones
sn: Jones
telephoneNumber: 444-555-6767
mail: jonesrd@acme.com
localityName: Houston
dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US
objectClass: inetOrgPerson
cn: Eric S. Woods
sn: Woods
telephoneNumber: 444-555-6768
mail: woodses@acme.com
localityName: Houston
4. 配置OpenLDAP
本文實(shí)踐了在 Windows 下安裝配 openldap,并添加一個(gè)條目,LdapBrowser 瀏覽,及 Java 程序連接 openldap 的全過(guò)程。
1. 下載安裝 openldap for windows,當(dāng)前版本2.2.29下載地址:http://download.bergmans.us/openldap/openldap-2.2.29/openldap-2.2.29-db-4.3.29-openssl-0.9.8a-win32_Setup.exe
相關(guān)鏈接:http://lucas.bergmans.us/hacks/openldap/
安裝很簡(jiǎn)單,一路 next 即可,假設(shè)我們安裝在 c:\openldap
2. 配置 openldap,編輯 sldap.conf 文件
1) 打開(kāi) c:\openldap\sldap.conf,找到
include C:/openldap/etc/schema/core.schema,在它后面添加
include C:/openldap/etc/schema/cosine.schema
include C:/openldap/etc/schema/inetorgperson.schema
接下來(lái)的例子只需要用到以上三個(gè) schema,當(dāng)然,如果你覺(jué)得需要的話,你可以把其他的 schema 全部添加進(jìn)來(lái)
include C:/openldap/etc/schema/corba.schema
include C:/openldap/etc/schema/dyngroup.schema
include C:/openldap/etc/schema/java.schema
include C:/openldap/etc/schema/misc.schema
include C:/openldap/etc/schema/nis.schema
include C:/openldap/etc/schema/openldap.schema
2) 還是在 sldap.conf 文件中,找到
suffix "dc=my-domain,dc=com"
rootdn "cn=Manager,dc=my-domain,dc=com"
把這兩行改為
suffix "o=teemlink,c=cn"
rootdn "cn=Manager,o=teemlink,dc=cn"
suffix 就是看自己如何定義了,后面步驟的 ldif 文件就必須與它定義了。還要注意到這個(gè)配置文件中有一個(gè) rootpw secret,這個(gè) secret 是 cn=Manager 的密碼,以后會(huì)用到,不過(guò)這里是明文密碼,你可以用命令: slappasswd -h {MD5} -s secret 算出加密的密碼 {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ== 取代配置中的 secret。
3. 啟動(dòng) openldap
CMD 進(jìn)入到 c:\openldap 下,運(yùn)行命令 sldapd -d 1
用可以看到控制臺(tái)下打印一片信息,openldap 默認(rèn)是用的 Berkeley DB 數(shù)據(jù)庫(kù)存儲(chǔ)目錄數(shù)據(jù)的。
4. 建立條目,編輯導(dǎo)入 ldif 文件
1) 新建一個(gè) ldif(LDAP Data Interchanged Format) 文件(純文本格式),例如 test.ldif,文件內(nèi)容如下:
dn: o=teemlink
objectclass: top
objectclass: organization
o: develop
2) 執(zhí)行命令:ldapadd -l test.ldif
5. 使用LDAP Browser進(jìn)行訪問(wèn)
5.1安裝LDAP Browser2.6軟件,進(jìn)行如下操作:


5.2顯示效果

5. Java操作LDAP
5.1 用JNDI進(jìn)訪問(wèn)
package cn.myapps.test;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
public class LdapTest {
public void JNDILookup() {
String root = "o=teemlink,c=cn";
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://192.168.0.30/" + root);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Nicholas,ou=develop,o=teemlink,c=cn");
env.put(Context.SECURITY_CREDENTIALS, "123456");
DirContext ctx = null;
try {
ctx = new InitialDirContext(env);
Attributes attrs = ctx.getAttributes("cn=Nicholas,ou=develop");
System.out.println("Last Name: " + attrs.get("sn").get());
System.out.println("認(rèn)證成功");
} catch (javax.naming.AuthenticationException e) {
e.printStackTrace();
System.out.println("認(rèn)證失敗");
} catch (Exception e) {
System.out.println("認(rèn)證出錯(cuò):");
e.printStackTrace();
}
if (ctx != null) {
try {
ctx.close();
} catch (NamingException e) {
// ignore
}
}
}
public static void main(String[] args) {
LdapTest LDAPTest = new LdapTest();
LDAPTest.JNDILookup();
}
}
5.2 用JLDAP進(jìn)訪問(wèn)
訪問(wèn)地址:http://www.openldap.org/jldap/ 并下載相關(guān)lib
import com.novell.ldap.*;
import java.io.UnsupportedEncodingException;
public class List
{
public static void main(String[] args)
{
int ldapPort = LDAPConnection.DEFAULT_PORT;
int searchScope = LDAPConnection.SCOPE_ONE;
int ldapVersion = LDAPConnection.LDAP_V3;
boolean attributeOnly = false;
String attrs[] = null;
String ldapHost = "192.168.0.30";
String loginDN = "cn=Manager,o=teemlink,c=cn";
String password = "secret";
String searchBase = "ou=develop,o=teemlink,c=cn";
String searchFilter = "objectClass=*";
LDAPConnection lc = new LDAPConnection();
try {
// connect to the server
lc.connect(ldapHost, ldapPort);
// bind to the server
lc.bind(ldapVersion, loginDN, password.getBytes("UTF8"));
LDAPSearchResults searchResults =
lc.search(searchBase, // container to search
searchScope, // search scope
searchFilter, // search filter
attrs, // "1.1" returns entry name only
attributeOnly); // no attributes are returned
// print out all the objects
while (searchResults.hasMore()) {
LDAPEntry nextEntry = null;
try {
nextEntry = searchResults.next();
System.out.println("\n" + nextEntry.getDN());
System.out.println(nextEntry.getAttributeSet());
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
// Exception is thrown, go for next entry
continue;
}
}
// disconnect with the server
lc.disconnect();
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
} catch (UnsupportedEncodingException e) {
System.out.println("Error: " + e.toString());
}
System.exit(0);
}
}
5.3 用JDBC-LDAP進(jìn)訪問(wèn)
訪問(wèn)地址:http://www.openldap.org/jdbcldap/ 并下載相關(guān)lib
package jdbcldap;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcLdap {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
String ldapConnectString = "jdbc:ldap://192.168.0.30/o=teemlink,c=cn?SEARCH_SCOPE:=subTreeScope";
Connection con = DriverManager.getConnection(ldapConnectString, "cn=Manager,o=teemlink,c=cn", "secret");
String sql = "SELECT * FROM ou=develop,o=teemlink,c=cn";
Statement sat = con.createStatement();
ResultSet rs = sta.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString(1));
}
if (con != null)
con.close();
}
}
原創(chuàng)人員:Nicholas
文章來(lái)源:
http://www.cnblogs.com/obpm/archive/2010/08/28/1811065.html
以子類(lèi)取代類(lèi)型編碼
Replace Type Code with Subclasses
1. 何謂重構(gòu)
1.1名詞解釋
對(duì)軟件內(nèi)部結(jié)構(gòu)的一種調(diào)整,目的是在不改變「軟件之可察行為」前提下,提高其可理解性,降低其修改成本。
1.2動(dòng)詞解釋
使用一系列重構(gòu)準(zhǔn)則(手法),在不改變「軟件之可察行為」前提下,調(diào)整其結(jié)構(gòu)。
2. 為何重構(gòu)
2.1「重構(gòu)」改進(jìn)軟件設(shè)計(jì)
同樣完成一件事,設(shè)計(jì)不良的程序往往需要更多代碼,這常常是因?yàn)榇a在不同的地方使用完全相同的語(yǔ)句做同樣的事。因此改進(jìn)設(shè)計(jì)的一個(gè)重要方向就是消除重復(fù)代碼(Duplicate Code)
2.2「重構(gòu)」使軟件更易被理解
你的源碼還有其它讀者:數(shù)個(gè)月之后可能會(huì)有另一位程序員嘗試讀懂你的代碼并做一些修改。我們很容易忘記這第二位讀者,但他才是最重要的。計(jì)算器是否多花了數(shù)個(gè)鐘頭進(jìn)行編譯,又有什么關(guān)系呢?如果一個(gè)程序員花費(fèi)一周時(shí)間來(lái)修改某段代碼,那才關(guān)系重大— 如果他理解你的代碼,這個(gè)修改原本只需一小時(shí)
2.3「重構(gòu)」助你找到臭蟲(chóng) ( bugs)
Kent Beck 經(jīng)常形容自己的一句話:『我不是個(gè)偉大的程序員;我只是個(gè)有著一些優(yōu)秀習(xí)慣的好程序員而已。』重構(gòu)能夠幫助我更有效地寫(xiě)出強(qiáng)固穩(wěn)健(robust)的代碼。
2.4「重構(gòu)」助你提高編程速度
終于,前面的一切都?xì)w結(jié)到了這最后一點(diǎn):重構(gòu)幫助你更快速地開(kāi)發(fā)程序。聽(tīng)起來(lái)有違反直覺(jué)。當(dāng)我談到重構(gòu),人們很容易看出它能夠提高質(zhì)量。改善設(shè)計(jì)、提升可讀性、減少錯(cuò)誤,這些都是提高質(zhì)量。但這難道不會(huì)降低開(kāi)發(fā)速度嗎?我強(qiáng)烈相信:良好設(shè)計(jì)是快速軟件開(kāi)發(fā)的根本。事實(shí)上擁有良好設(shè)計(jì)才可能達(dá)成快速的開(kāi)發(fā)。如果沒(méi)有良好設(shè)計(jì),或許某一段時(shí)間內(nèi)你的進(jìn)展迅速,但惡劣的設(shè)計(jì)很快就讓你的速度慢下來(lái)。你會(huì)把時(shí)間花在調(diào)試上面,無(wú)法添加新功能。修改時(shí)間愈來(lái)愈長(zhǎng),因?yàn)槟惚仨毣ㄓ鷣?lái)愈多的時(shí)間去理解系統(tǒng)、尋找重復(fù)代碼。隨著你給最初程序打上一個(gè)又一個(gè)的補(bǔ)丁(patch),新特性需要更多代碼才能實(shí)現(xiàn)。真是個(gè)惡性循環(huán)。
3.何時(shí)重構(gòu)?
重構(gòu)本來(lái)就不是一件「特別撥出時(shí)間做」的事情,重構(gòu)應(yīng)該隨時(shí)隨地進(jìn)行。你不應(yīng)該為重構(gòu)而重構(gòu),你之所以重構(gòu),是因?yàn)槟阆胱鰟e的什么事,而重構(gòu)可以幫助你把那些事做好
3.1三次法則(The Rule of Three)
Don Roberts 給了我一條準(zhǔn)則:第一次做某件事時(shí)只管去做;第二次做類(lèi)似的事會(huì)產(chǎn)生反感,但無(wú)論如何還是做了;第三次再做類(lèi)似的事,你就應(yīng)該重構(gòu)。
☆ 事不過(guò)三,三則重構(gòu)。(Three strikes and you refactor.)
3.2重構(gòu)時(shí)機(jī)
添加功能時(shí)一并重構(gòu)
修補(bǔ)錯(cuò)誤時(shí)一并重構(gòu)
復(fù)審代碼時(shí)一并重構(gòu)
-以上章節(jié)摘抄自《重構(gòu)-改善既有代碼的設(shè)計(jì)》
4.平臺(tái)重構(gòu)案例
4.1重構(gòu)動(dòng)機(jī)
1. 實(shí)例模塊為View,重構(gòu)元素為ViewAction、ViewProcessBean、View,以下為關(guān)系圖。

2. 由于View存在多種editMode(編輯模式),而每個(gè)調(diào)用的地方都需要進(jìn)行type code(類(lèi)型碼)判斷,然后再進(jìn)行相應(yīng)的業(yè)務(wù)邏輯處理,最終在每個(gè)調(diào)用的地方都形成了大量的if-else代碼,大大減弱了代碼的可讀性,和邏輯清晰度。
3. 調(diào)用的地方:

4.2重構(gòu)作法

如上圖所示,將每種type code重構(gòu)成subclass,加強(qiáng)了每種類(lèi)型處理業(yè)務(wù)邏輯的能力。
View-版本1566代碼片段:
public EditMode getEditModeType() {
if (EDIT_MODE_CODE_DQL.equals(getEditMode())) {
return new DQLEditMode(this);
} else if (EDIT_MODE_CODE_SQL.equals(getEditMode())) {
return new SQLEditMode(this);
} else if (EDIT_MODE_DESIGN.equals(getEditMode())) {
return new DesignEditMode(this);
}
return new NullEditMode(this);
}
說(shuō)明:調(diào)用者無(wú)需了解具體的類(lèi)型,由View自身作判斷,返回EditMode接口,從而實(shí)現(xiàn)多態(tài)調(diào)用。
ViewProcessBean代碼片段:
重構(gòu)前-版本1503:
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
if (view.getEditMode().equals(View.EDIT_MODE_DESIGN)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().equals(View.EDIT_MODE_CODE_DQL)) {
datas = dp.queryByDQLPage(dql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().endsWith(View.EDIT_MODE_CODE_SQL)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
}
}
重構(gòu)后-版本1566:
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
datas = view.getEditModeType().getDataPackage(params, tempPage, LINES, user, currdoc);
//其他業(yè)務(wù)邏輯
}
5.結(jié)語(yǔ)
由上述案例可看到,引入subclass代替type code可以大大減少if-else判斷,而且可以把責(zé)任內(nèi)聚到每種type中,使代碼結(jié)構(gòu)更清晰易懂。
在本案例中引入了空類(lèi)型概念,即NullEditMode,代碼如下:
/**
*
* @author nicholas zhen
*
*/
public class NullEditMode extends AbstractEditMode implements EditMode {
public NullEditMode(View view) {
super(view);
}
public String getQueryString(ParamsTable params, WebUser user, Document sDoc) {
return "";
}
public DataPackage getDataPackage(ParamsTable params, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public DataPackage getDataPackage(ParamsTable params, int page, int lines, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public long count(ParamsTable params, WebUser user, Document doc) throws Exception {
return 0;
}
}
說(shuō)明:空類(lèi)型保證了調(diào)用每個(gè)方法都有默認(rèn)值返回,而不需要進(jìn)行非空判斷,當(dāng)View沒(méi)有類(lèi)型時(shí),即返回默認(rèn)的空類(lèi)型
原創(chuàng)人員:Nicholas
文章來(lái)源:
http://www.cnblogs.com/obpm/archive/2010/07/13/1776856.html
不會(huì)畫(huà)流程圖的,看了這個(gè)就懂了,絕對(duì)經(jīng)典!

轉(zhuǎn)載人員:Nicholas
文章來(lái)源:
http://www.cnblogs.com/obpm/archive/2010/07/12/1776058.html
創(chuàng)建型模式 1、FACTORY —追MM少不了請(qǐng)吃飯了,麥當(dāng)勞的雞翅和肯德基的雞翅都是MM愛(ài)吃的東西,雖然口味有所不同,但不管你帶MM去麥當(dāng)勞或肯德基,只管向服務(wù)員說(shuō)“來(lái)四個(gè)雞翅”就行了。麥當(dāng)勞和肯德基就是生產(chǎn)雞翅的Factory
工廠模式:客戶(hù)類(lèi)和工廠類(lèi)分開(kāi)。消費(fèi)者任何時(shí)候需要某種產(chǎn)品,只需向工廠請(qǐng)求即可。消費(fèi)者無(wú)須修改就可以接納新產(chǎn)品。缺點(diǎn)是當(dāng)產(chǎn)品修改時(shí),工廠類(lèi)也要做相應(yīng)的修改。如:如何創(chuàng)建及如何向客戶(hù)端提供。
2、BUILDER —MM最?lèi)?ài)聽(tīng)的就是“我愛(ài)你”這句話了,見(jiàn)到不同地方的MM,要能夠用她們的方言跟她說(shuō)這句話哦,我有一個(gè)多種語(yǔ)言翻譯機(jī),上面每種語(yǔ)言都有一個(gè)按鍵,見(jiàn)到MM我只要按對(duì)應(yīng)的鍵,它就能夠用相應(yīng)的語(yǔ)言說(shuō)出“我愛(ài)你”這句話了,國(guó)外的MM也可以輕松搞掂,這就是我的“我愛(ài)你”builder。(這一定比美軍在伊拉克用的翻譯機(jī)好賣(mài))
建造模式:將產(chǎn)品的內(nèi)部表象和產(chǎn)品的生成過(guò)程分割開(kāi)來(lái),從而使一個(gè)建造過(guò)程生成具有不同的內(nèi)部表象的產(chǎn)品對(duì)象。建造模式使得產(chǎn)品內(nèi)部表象可以獨(dú)立的變化,客戶(hù)不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。建造模式可以強(qiáng)制實(shí)行一種分步驟進(jìn)行的建造過(guò)程。
3、FACTORY METHOD —請(qǐng)MM去麥當(dāng)勞吃漢堡,不同的MM有不同的口味,要每個(gè)都記住是一件煩人的事情,我一般采用Factory Method模式,帶著MM到服務(wù)員那兒,說(shuō)“要一個(gè)漢堡”,具體要什么樣的漢堡呢,讓MM直接跟服務(wù)員說(shuō)就行了。
工廠方法模式:核心工廠類(lèi)不再負(fù)責(zé)所有產(chǎn)品的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類(lèi)去做,成為一個(gè)抽象工廠角色,僅負(fù)責(zé)給出具體工廠類(lèi)必須實(shí)現(xiàn)的接口,而不接觸哪一個(gè)產(chǎn)品類(lèi)應(yīng)當(dāng)被實(shí)例化這種細(xì)節(jié)。
4、PROTOTYPE —跟MM用QQ聊天,一定要說(shuō)些深情的話語(yǔ)了,我搜集了好多肉麻的情話,需要時(shí)只要copy出來(lái)放到QQ里面就行了,這就是我的情話prototype了。(100塊錢(qián)一份,你要不要)
原始模型模式:通過(guò)給出一個(gè)原型對(duì)象來(lái)指明所要?jiǎng)?chuàng)建的對(duì)象的類(lèi)型,然后用復(fù)制這個(gè)原型對(duì)象的方法創(chuàng)建出更多同類(lèi)型的對(duì)象。原始模型模式允許動(dòng)態(tài)的增加或減少產(chǎn)品類(lèi),產(chǎn)品類(lèi)不需要非得有任何事先確定的等級(jí)結(jié)構(gòu),原始模型模式適用于任何的等級(jí)結(jié)構(gòu)。缺點(diǎn)是每一個(gè)類(lèi)都必須配備一個(gè)克隆方法。
5、SINGLETON —俺有6個(gè)漂亮的老婆,她們的老公都是我,我就是我們家里的老公Sigleton,她們只要說(shuō)道“老公”,都是指的同一個(gè)人,那就是我(剛才做了個(gè)夢(mèng)啦,哪有這么好的事)
單例模式:?jiǎn)卫J酱_保某一個(gè)類(lèi)只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例單例模式。單例模式只應(yīng)在有真正的“單一實(shí)例”的需求時(shí)才可使用。
結(jié)構(gòu)型模式
6、ADAPTER —在朋友聚會(huì)上碰到了一個(gè)美女Sarah,從香港來(lái)的,可我不會(huì)說(shuō)粵語(yǔ),她不會(huì)說(shuō)普通話,只好求助于我的朋友kent了,他作為我和Sarah之間的Adapter,讓我和Sarah可以相互交談了(也不知道他會(huì)不會(huì)耍我)
適配器模式:把一個(gè)類(lèi)的接口變換成客戶(hù)端所期待的另一種接口,從而使原本因接口原因不匹配而無(wú)法一起工作的兩個(gè)類(lèi)能夠一起工作。適配類(lèi)可以根據(jù)參數(shù)返還一個(gè)合適的實(shí)例給客戶(hù)端。
7、BRIDGE —早上碰到MM,要說(shuō)早上好,晚上碰到MM,要說(shuō)晚上好;碰到MM穿了件新衣服,要說(shuō)你的衣服好漂亮哦,碰到MM新做的發(fā)型,要說(shuō)你的頭發(fā)好漂亮哦。不要問(wèn)我“早上碰到MM新做了個(gè)發(fā)型怎么說(shuō)”這種問(wèn)題,自己用BRIDGE組合一下不就行了
橋梁模式:將抽象化與實(shí)現(xiàn)化脫耦,使得二者可以獨(dú)立的變化,也就是說(shuō)將他們之間的強(qiáng)關(guān)聯(lián)變成弱關(guān)聯(lián),也就是指在一個(gè)軟件系統(tǒng)的抽象化和實(shí)現(xiàn)化之間使用組合/聚合關(guān)系而不是繼承關(guān)系,從而使兩者可以獨(dú)立的變化。
8、COMPOSITE —Mary今天過(guò)生日。“我過(guò)生日,你要送我一件禮物。”“嗯,好吧,去商店,你自己挑。”“這件T恤挺漂亮,買(mǎi),這條裙子好看,買(mǎi),這個(gè)包也不錯(cuò),買(mǎi)。”“喂,買(mǎi)了三件了呀,我只答應(yīng)送一件禮物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻煩你包起來(lái)。”“……”,MM都會(huì)用Composite模式了,你會(huì)了沒(méi)有?
合成模式:合成模式將對(duì)象組織到樹(shù)結(jié)構(gòu)中,可以用來(lái)描述整體與部分的關(guān)系。合成模式就是一個(gè)處理對(duì)象的樹(shù)結(jié)構(gòu)的模式。合成模式把部分與整體的關(guān)系用樹(shù)結(jié)構(gòu)表示出來(lái)。合成模式使得客戶(hù)端把一個(gè)個(gè)單獨(dú)的成分對(duì)象和由他們復(fù)合而成的合成對(duì)象同等看待。
9、DECORATOR —Mary過(guò)完輪到Sarly過(guò)生日,還是不要叫她自己挑了,不然這個(gè)月伙食費(fèi)肯定玩完,拿出我去年在華山頂上照的照片,在背面寫(xiě)上“最好的的禮物,就是愛(ài)你的Fita”,再到街上禮品店買(mǎi)了個(gè)像框(賣(mài)禮品的MM也很漂亮哦),再找隔壁搞美術(shù)設(shè)計(jì)的Mike設(shè)計(jì)了一個(gè)漂亮的盒子裝起來(lái)……,我們都是Decorator,最終都在修飾我這個(gè)人呀,怎么樣,看懂了嗎?
裝飾模式:裝飾模式以對(duì)客戶(hù)端透明的方式擴(kuò)展對(duì)象的功能,是繼承關(guān)系的一個(gè)替代方案,提供比繼承更多的靈活性。動(dòng)態(tài)給一個(gè)對(duì)象增加功能,這些功能可以再動(dòng)態(tài)的撤消。增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能。
10、FAÇADE —我有一個(gè)專(zhuān)業(yè)的Nikon相機(jī),我就喜歡自己手動(dòng)調(diào)光圈、快門(mén),這樣照出來(lái)的照片才專(zhuān)業(yè),但MM可不懂這些,教了半天也不會(huì)。幸好相機(jī)有Facade設(shè)計(jì)模式,把相機(jī)調(diào)整到自動(dòng)檔,只要對(duì)準(zhǔn)目標(biāo)按快門(mén)就行了,一切由相機(jī)自動(dòng)調(diào)整,這樣MM也可以用這個(gè)相機(jī)給我拍張照片了。
門(mén)面模式:外部與一個(gè)子系統(tǒng)的通信必須通過(guò)一個(gè)統(tǒng)一的門(mén)面對(duì)象進(jìn)行。門(mén)面模式提供一個(gè)高層次的接口,使得子系統(tǒng)更易于使用。每一個(gè)子系統(tǒng)只有一個(gè)門(mén)面類(lèi),而且此門(mén)面類(lèi)只有一個(gè)實(shí)例,也就是說(shuō)它是一個(gè)單例模式。但整個(gè)系統(tǒng)可以有多個(gè)門(mén)面類(lèi)。
11、FLYWEIGHT —每天跟MM發(fā)短信,手指都累死了,最近買(mǎi)了個(gè)新手機(jī),可以把一些常用的句子存在手機(jī)里,要用的時(shí)候,直接拿出來(lái),在前面加上MM的名字就可以發(fā)送了,再不用一個(gè)字一個(gè)字敲了。共享的句子就是Flyweight,MM的名字就是提取出來(lái)的外部特征,根據(jù)上下文情況使用。
享元模式:FLYWEIGHT在拳擊比賽中指最輕量級(jí)。享元模式以共享的方式高效的支持大量的細(xì)粒度對(duì)象。享元模式能做到共享的關(guān)鍵是區(qū)分內(nèi)蘊(yùn)狀態(tài)和外蘊(yùn)狀態(tài)。內(nèi)蘊(yùn)狀態(tài)存儲(chǔ)在享元內(nèi)部,不會(huì)隨環(huán)境的改變而有所不同。外蘊(yùn)狀態(tài)是隨環(huán)境的改變而改變的。外蘊(yùn)狀態(tài)不能影響內(nèi)蘊(yùn)狀態(tài),它們是相互獨(dú)立的。將可以共享的狀態(tài)和不可以共享的狀態(tài)從常規(guī)類(lèi)中區(qū)分開(kāi)來(lái),將不可以共享的狀態(tài)從類(lèi)里剔除出去。客戶(hù)端不可以直接創(chuàng)建被共享的對(duì)象,而應(yīng)當(dāng)使用一個(gè)工廠對(duì)象負(fù)責(zé)創(chuàng)建被共享的對(duì)象。享元模式大幅度的降低內(nèi)存中對(duì)象的數(shù)量。
12、PROXY —跟MM在網(wǎng)上聊天,一開(kāi)頭總是“hi,你好”,“你從哪兒來(lái)呀?”“你多大了?”“身高多少呀?”這些話,真煩人,寫(xiě)個(gè)程序做為我的Proxy吧,凡是接收到這些話都設(shè)置好了自動(dòng)的回答,接收到其他的話時(shí)再通知我回答,怎么樣,酷吧。
代理模式:代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)源對(duì)象的引用。代理就是一個(gè)人或一個(gè)機(jī)構(gòu)代表另一個(gè)人或者一個(gè)機(jī)構(gòu)采取行動(dòng)。某些情況下,客戶(hù)不想或者不能夠直接引用一個(gè)對(duì)象,代理對(duì)象可以在客戶(hù)和目標(biāo)對(duì)象直接起到中介的作用。客戶(hù)端分辨不出代理主題對(duì)象與真實(shí)主題對(duì)象。代理模式可以并不知道真正的被代理對(duì)象,而僅僅持有一個(gè)被代理對(duì)象的接口,這時(shí)候代理對(duì)象不能夠創(chuàng)建被代理對(duì)象,被代理對(duì)象必須有系統(tǒng)的其他角色代為創(chuàng)建并傳入。
行為模式
13、CHAIN OF RESPONSIBLEITY —晚上去上英語(yǔ)課,為了好開(kāi)溜坐到了最后一排,哇,前面坐了好幾個(gè)漂亮的MM哎,找張紙條,寫(xiě)上“Hi,可以做我的女朋友嗎?如果不愿意請(qǐng)向前傳”,紙條就一個(gè)接一個(gè)的傳上去了,糟糕,傳到第一排的MM把紙條傳給老師了,聽(tīng)說(shuō)是個(gè)老處女呀,快跑!
責(zé)任鏈模式:在責(zé)任鏈模式中,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而接起來(lái)形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞,直到鏈上的某一個(gè)對(duì)象決定處理此請(qǐng)求。客戶(hù)并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,系統(tǒng)可以在不影響客戶(hù)端的情況下動(dòng)態(tài)的重新組織鏈和分配責(zé)任。處理者有兩個(gè)選擇:承擔(dān)責(zé)任或者把責(zé)任推給下家。一個(gè)請(qǐng)求可以最終不被任何接收端對(duì)象所接受。
14、COMMAND —俺有一個(gè)MM家里管得特別嚴(yán),沒(méi)法見(jiàn)面,只好借助于她弟弟在我們倆之間傳送信息,她對(duì)我有什么指示,就寫(xiě)一張紙條讓她弟弟帶給我。這不,她弟弟又傳送過(guò)來(lái)一個(gè)COMMAND,為了感謝他,我請(qǐng)他吃了碗雜醬面,哪知道他說(shuō):“我同時(shí)給我姐姐三個(gè)男朋友送COMMAND,就數(shù)你最小氣,才請(qǐng)我吃面。”,
命令模式:命令模式把一個(gè)請(qǐng)求或者操作封裝到一個(gè)對(duì)象中。命令模式把發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分割開(kāi),委派給不同的對(duì)象。命令模式允許請(qǐng)求的一方和發(fā)送的一方獨(dú)立開(kāi)來(lái),使得請(qǐng)求的一方不必知道接收請(qǐng)求的一方的接口,更不必知道請(qǐng)求是怎么被接收,以及操作是否執(zhí)行,何時(shí)被執(zhí)行以及是怎么被執(zhí)行的。系統(tǒng)支持命令的撤消。
15、INTERPRETER —俺有一個(gè)《泡MM真經(jīng)》,上面有各種泡MM的攻略,比如說(shuō)去吃西餐的步驟、去看電影的方法等等,跟MM約會(huì)時(shí),只要做一個(gè)Interpreter,照著上面的腳本執(zhí)行就可以了。
解釋器模式:給定一個(gè)語(yǔ)言后,解釋器模式可以定義出其文法的一種表示,并同時(shí)提供一個(gè)解釋器。客戶(hù)端可以使用這個(gè)解釋器來(lái)解釋這個(gè)語(yǔ)言中的句子。解釋器模式將描述怎樣在有了一個(gè)簡(jiǎn)單的文法后,使用模式設(shè)計(jì)解釋這些語(yǔ)句。在解釋器模式里面提到的語(yǔ)言是指任何解釋器對(duì)象能夠解釋的任何組合。在解釋器模式中需要定義一個(gè)代表文法的命令類(lèi)的等級(jí)結(jié)構(gòu),也就是一系列的組合規(guī)則。每一個(gè)命令對(duì)象都有一個(gè)解釋方法,代表對(duì)命令對(duì)象的解釋。命令對(duì)象的等級(jí)結(jié)構(gòu)中的對(duì)象的任何排列組合都是一個(gè)語(yǔ)言。
16、ITERATOR —我愛(ài)上了Mary,不顧一切的向她求婚。
Mary:“想要我跟你結(jié)婚,得答應(yīng)我的條件”
我:“什么條件我都答應(yīng),你說(shuō)吧”
Mary:“我看上了那個(gè)一克拉的鉆石”
我:“我買(mǎi),我買(mǎi),還有嗎?”
Mary:“我看上了湖邊的那棟別墅”
我:“我買(mǎi),我買(mǎi),還有嗎?”
Mary:“你的小弟弟必須要有
50cm
長(zhǎng)”
我腦袋嗡的一聲,坐在椅子上,一咬牙:“我剪,我剪,還有嗎?”
……
迭代子模式:迭代子模式可以順序訪問(wèn)一個(gè)聚集中的元素而不必暴露聚集的內(nèi)部表象。多個(gè)對(duì)象聚在一起形成的總體稱(chēng)之為聚集,聚集對(duì)象是能夠包容一組對(duì)象的容器對(duì)象。迭代子模式將迭代邏輯封裝到一個(gè)獨(dú)立的子對(duì)象中,從而與聚集本身隔開(kāi)。迭代子模式簡(jiǎn)化了聚集的界面。每一個(gè)聚集對(duì)象都可以有一個(gè)或一個(gè)以上的迭代子對(duì)象,每一個(gè)迭代子的迭代狀態(tài)可以是彼此獨(dú)立的。迭代算法可以獨(dú)立于聚集角色變化。
17、MEDIATOR —四個(gè)MM打麻將,相互之間誰(shuí)應(yīng)該給誰(shuí)多少錢(qián)算不清楚了,幸虧當(dāng)時(shí)我在旁邊,按照各自的籌碼數(shù)算錢(qián),賺了錢(qián)的從我這里拿,賠了錢(qián)的也付給我,一切就OK啦,俺得到了四個(gè)MM的電話。
調(diào)停者模式:調(diào)停者模式包裝了一系列對(duì)象相互作用的方式,使得這些對(duì)象不必相互明顯作用。從而使他們可以松散偶合。當(dāng)某些對(duì)象之間的作用發(fā)生改變時(shí),不會(huì)立即影響其他的一些對(duì)象之間的作用。保證這些作用可以彼此獨(dú)立的變化。調(diào)停者模式將多對(duì)多的相互作用轉(zhuǎn)化為一對(duì)多的相互作用。調(diào)停者模式將對(duì)象的行為和協(xié)作抽象化,把對(duì)象在小尺度的行為上與其他對(duì)象的相互作用分開(kāi)處理。
18、MEMENTO —同時(shí)跟幾個(gè)MM聊天時(shí),一定要記清楚剛才跟MM說(shuō)了些什么話,不然MM發(fā)現(xiàn)了會(huì)不高興的哦,幸虧我有個(gè)備忘錄,剛才與哪個(gè)MM說(shuō)了什么話我都拷貝一份放到備忘錄里面保存,這樣可以隨時(shí)察看以前的記錄啦。
備忘錄模式:備忘錄對(duì)象是一個(gè)用來(lái)存儲(chǔ)另外一個(gè)對(duì)象內(nèi)部狀態(tài)的快照的對(duì)象。備忘錄模式的用意是在不破壞封裝的條件下,將一個(gè)對(duì)象的狀態(tài)捉住,并外部化,存儲(chǔ)起來(lái),從而可以在將來(lái)合適的時(shí)候把這個(gè)對(duì)象還原到存儲(chǔ)起來(lái)的狀態(tài)。
19、OBSERVER —想知道咱們公司最新MM情報(bào)嗎?加入公司的MM情報(bào)郵件組就行了,tom負(fù)責(zé)搜集情報(bào),他發(fā)現(xiàn)的新情報(bào)不用一個(gè)一個(gè)通知我們,直接發(fā)布給郵件組,我們作為訂閱者(觀察者)就可以及時(shí)收到情報(bào)啦
觀察者模式:觀察者模式定義了一種一隊(duì)多的依賴(lài)關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽(tīng)某一個(gè)主題對(duì)象。這個(gè)主題對(duì)象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,使他們能夠自動(dòng)更新自己。
20、STATE —跟MM交往時(shí),一定要注意她的狀態(tài)哦,在不同的狀態(tài)時(shí)她的行為會(huì)有不同,比如你約她今天晚上去看電影,對(duì)你沒(méi)興趣的MM就會(huì)說(shuō)“有事情啦”,對(duì)你不討厭但還沒(méi)喜歡上的MM就會(huì)說(shuō)“好啊,不過(guò)可以帶上我同事么?”,已經(jīng)喜歡上你的MM就會(huì)說(shuō)“幾點(diǎn)鐘?看完電影再去泡吧怎么樣?”,當(dāng)然你看電影過(guò)程中表現(xiàn)良好的話,也可以把MM的狀態(tài)從不討厭不喜歡變成喜歡哦。
狀態(tài)模式:狀態(tài)模式允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候改變行為。這個(gè)對(duì)象看上去象是改變了它的類(lèi)一樣。狀態(tài)模式把所研究的對(duì)象的行為包裝在不同的狀態(tài)對(duì)象里,每一個(gè)狀態(tài)對(duì)象都屬于一個(gè)抽象狀態(tài)類(lèi)的一個(gè)子類(lèi)。狀態(tài)模式的意圖是讓一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候,其行為也隨之改變。狀態(tài)模式需要對(duì)每一個(gè)系統(tǒng)可能取得的狀態(tài)創(chuàng)立一個(gè)狀態(tài)類(lèi)的子類(lèi)。當(dāng)系統(tǒng)的狀態(tài)變化時(shí),系統(tǒng)便改變所選的子類(lèi)。
21、STRATEGY —跟不同類(lèi)型的MM約會(huì),要用不同的策略,有的請(qǐng)電影比較好,有的則去吃小吃效果不錯(cuò),有的去海邊浪漫最合適,單目的都是為了得到MM的芳心,我的追MM錦囊中有好多Strategy哦。
策略模式:策略模式針對(duì)一組算法,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類(lèi)中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶(hù)端的情況下發(fā)生變化。策略模式把行為和環(huán)境分開(kāi)。環(huán)境類(lèi)負(fù)責(zé)維持和查詢(xún)行為類(lèi),各種算法在具體的策略類(lèi)中提供。由于算法和環(huán)境獨(dú)立開(kāi)來(lái),算法的增減,修改都不會(huì)影響到環(huán)境和客戶(hù)端。
22、TEMPLATE METHOD ——看過(guò)《如何說(shuō)服女生上床》這部經(jīng)典文章嗎?女生從認(rèn)識(shí)到上床的不變的步驟分為巧遇、打破僵局、展開(kāi)追求、接吻、前戲、動(dòng)手、愛(ài)撫、進(jìn)去八大步驟(Template method),但每個(gè)步驟針對(duì)不同的情況,都有不一樣的做法,這就要看你隨機(jī)應(yīng)變啦(具體實(shí)現(xiàn));
模板方法模式:模板方法模式準(zhǔn)備一個(gè)抽象類(lèi),將部分邏輯以具體方法以及具體構(gòu)造子的形式實(shí)現(xiàn),然后聲明一些抽象方法來(lái)迫使子類(lèi)實(shí)現(xiàn)剩余的邏輯。不同的子類(lèi)可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對(duì)剩余的邏輯有不同的實(shí)現(xiàn)。先制定一個(gè)頂級(jí)邏輯框架,而將邏輯的細(xì)節(jié)留給具體的子類(lèi)去實(shí)現(xiàn)。
23、VISITOR —情人節(jié)到了,要給每個(gè)MM送一束鮮花和一張卡片,可是每個(gè)MM送的花都要針對(duì)她個(gè)人的特點(diǎn),每張卡片也要根據(jù)個(gè)人的特點(diǎn)來(lái)挑,我一個(gè)人哪搞得清楚,還是找花店老板和禮品店老板做一下Visitor,讓花店老板根據(jù)MM的特點(diǎn)選一束花,讓禮品店老板也根據(jù)每個(gè)人特點(diǎn)選一張卡,這樣就輕松多了;
訪問(wèn)者模式:訪問(wèn)者模式的目的是封裝一些施加于某種數(shù)據(jù)結(jié)構(gòu)元素之上的操作。一旦這些操作需要修改的話,接受這個(gè)操作的數(shù)據(jù)結(jié)構(gòu)可以保持不變。訪問(wèn)者模式適用于數(shù)據(jù)結(jié)構(gòu)相對(duì)未定的系統(tǒng),它把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作之間的耦合解脫開(kāi),使得操作集合可以相對(duì)自由的演化。訪問(wèn)者模式使得增加新的操作變的很容易,就是增加一個(gè)新的訪問(wèn)者類(lèi)。訪問(wèn)者模式將有關(guān)的行為集中到一個(gè)訪問(wèn)者對(duì)象中,而不是分散到一個(gè)個(gè)的節(jié)點(diǎn)類(lèi)中。當(dāng)使用訪問(wèn)者模式時(shí),要將盡可能多的對(duì)象瀏覽邏輯放在訪問(wèn)者類(lèi)中,而不是放到它的子類(lèi)中。訪問(wèn)者模式可以跨過(guò)幾個(gè)類(lèi)的等級(jí)結(jié)構(gòu)訪問(wèn)屬于不同的等級(jí)結(jié)構(gòu)的成員類(lèi)。
轉(zhuǎn)載人員:Nicholas
文章來(lái)源:
http://www.cnblogs.com/obpm/archive/2010/07/12/1776045.html
obpm一鍵生成視圖功能原理
在obpm系統(tǒng)后臺(tái)表單右上角有一個(gè)“一鍵生成視圖”功能。實(shí)現(xiàn)它的真正目的是為了后臺(tái)管理人員方便從實(shí)現(xiàn)好的表單中快速生成所有帶值的列的視圖。這樣管理人員就不需要手工新建視圖,然后再添加視圖中的帶值的列。
實(shí)現(xiàn)原理圖:

在實(shí)現(xiàn)原理圖中,我們發(fā)現(xiàn)沒(méi)有視圖中并沒(méi)有不帶值Field4相應(yīng)的Column4在視圖中,這是因?yàn)樵谝晥D中是要根據(jù)不同Column顯示不同的值的。如果Column是不帶值的話,那么視圖中就不應(yīng)該要這個(gè)Column,即使是要了,在視圖中沒(méi)有意義了。
實(shí)現(xiàn)原理代碼:
其中代碼路徑是:src-java-cn-myapps-core-dynaform-form-ejb-FormProcessBean.java
/**
* 根據(jù)表單編號(hào)來(lái)生成視圖
* @param formid 表單編號(hào)
* @throws Exception
*/
public Form oneKeyCreateView(String formid) throws Exception {
FormProcess formPross = (FormProcess) ProcessFactory.createProcess(FormProcess.class);
ViewProcess viewPross = (ViewProcess) ProcessFactory.createProcess(ViewProcess.class);
Form form = (Form) formPross.doView(formid);//獲得form
Collection formfield=form.getValueStoreFields();//獲得form存儲(chǔ)值的field
//新建視圖
View view = new View();
if (view.getId() == null || view.getId().trim().length() <= 0) {
view.setId(Sequence.getSequence());//設(shè)置視圖的ID
view.setSortId(Sequence.getTimeSequence());//設(shè)置視圖的排序ID }
view.setName(form.getName());//把表單的名字賦給視圖
view.setOpenType(view.OPEN_TYPE_NORMAL); //設(shè)置視圖打開(kāi)類(lèi)型-普通類(lèi)型
view.setLastmodifytime(new Date());//最后修改日期
view.setApplicationid(form.getApplicationid());//把表單應(yīng)用程序Id賦給視圖的應(yīng)用程序Id
view.setModule(form.getModule());//把表單模塊Id賦給視圖的模塊ID
view.setPagelines("10");//設(shè)置視圖的分頁(yè)每頁(yè)顯示10條數(shù)據(jù)
view.setShowTotalRow(true); //是否顯示總共條數(shù)數(shù)據(jù)
view.setPagination(true); //是否分頁(yè)顯示
view.setRelatedForm(form.getId());//把表單ID賦給視圖的映射表單,從而映射了該表單
//將表單中對(duì)應(yīng)有值的列轉(zhuǎn)換為視圖的列
int i=0;
for(Iterator iterator=formfield.iterator();iterator.hasNext();){
FormField field=(FormField)iterator.next();
Column column = new Column();
if (column.getId() == null || column.getId().trim().length() <= 0) {
column.setId(Sequence.getSequence());
column.setOrderno(i);
}
if(field.getDiscript()!=null && !field.getDiscript().equals("")){//如果該表單中帶值Field有描述的話,就作為視圖Column,否則的用Field名稱(chēng)
column.setName(field.getDiscript());
}else{
column.setName(field.getName());
}
column.setFormid(form.getId());//把表單中的ID賦給Column的表單ID
column.setApplicationid(form.getApplicationid());//把表單中應(yīng)用程序的ID賦給Column的表單應(yīng)用程序ID
column.setFieldName(field.getName()); //把表單中的名稱(chēng)賦給Column的表單名稱(chēng)
column.setParentView(view.getId());//將視圖ID賦給Column的父視圖
view.getColumns().add(column); //將視圖和Column關(guān)聯(lián)
i++;
}
//分別創(chuàng)建兩個(gè)按鈕 新建,刪除
Activity activityCreate = new Activity();
if (activityCreate.getId() == null || activityCreate.getId().trim().length() <= 0) {
activityCreate.setId(Sequence.getSequence());
activityCreate.setOrderno(0);
}
activityCreate.setApplicationid(form.getApplicationid());
activityCreate.setName("新建");
activityCreate.setParentView(view.getId());
activityCreate.setType(ActivityType.DOCUMENT_CREATE);
activityCreate.setOnActionForm(form.getId());
view.getActivitys().add(activityCreate); //將視圖和新建按鈕關(guān)聯(lián)
Activity activityDelete = new Activity();
if (activityDelete.getId() == null || activityDelete.getId().trim().length() <= 0) {
activityDelete.setId(Sequence.getSequence());
activityDelete.setOrderno(1);
}
activityDelete.setApplicationid(form.getApplicationid());
activityDelete.setName("刪除");
activityDelete.setParentView(view.getId());
activityDelete.setType(ActivityType.DOCUMENT_DELETE);
view.getActivitys().add(activityDelete); //將視圖和刪除按鈕關(guān)聯(lián)
viewPross.doCreate(view); //創(chuàng)建視圖
return form;
}
后臺(tái)效果圖:
表單:

視圖:

視圖列:

視圖按鈕:

前臺(tái)效果:
視圖:

表單:

原創(chuàng)人員:Denny
文章來(lái)源:
http://www.cnblogs.com/obpm/archive/2010/07/12/1775453.html
鎖( locking )
業(yè)務(wù)邏輯的實(shí)現(xiàn)過(guò)程中,往往需要保證數(shù)據(jù)訪問(wèn)的排他性。如在金融系統(tǒng)的日終結(jié)算處理中,我們希望針對(duì)某個(gè) cut-off 時(shí)間點(diǎn)的數(shù)據(jù)進(jìn)行處理,而不希望在結(jié)算進(jìn)行過(guò)程中(可能是幾秒種,也可能是幾個(gè)小時(shí)),數(shù)據(jù)再發(fā)生變化。此時(shí),我們就需要通過(guò)一些機(jī)制來(lái)保證這些數(shù)據(jù)在某個(gè)操作過(guò)程中不會(huì)被外界修改,這樣的機(jī)制,在這里,也就是所謂的 “鎖” ,即給我們選定的目標(biāo)數(shù)據(jù)上鎖,使其無(wú)法被其他程序修改。Hibernate 支持兩種鎖機(jī)制:即通常所說(shuō)的 “悲觀鎖( Pessimistic Locking )”和 “樂(lè)觀鎖( Optimistic Locking )” 。
悲觀鎖( Pessimistic Locking )
悲觀鎖,正如其名,它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來(lái)自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此,在整個(gè)數(shù)據(jù)處理過(guò)程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制(也只有數(shù)據(jù)庫(kù)層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問(wèn)的排他性,否則,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無(wú)法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù))。
一個(gè)典型的倚賴(lài)數(shù)據(jù)庫(kù)的悲觀鎖調(diào)用:
select * from account where name=”Erica” for update
這條 sql 語(yǔ)句鎖定了 account 表中所有符合檢索條件(name=”Erica”)的記錄。本次事務(wù)提交之前(事務(wù)提交時(shí)會(huì)釋放事務(wù)過(guò)程中的鎖),外界無(wú)法修改這些記錄。Hibernate 的悲觀鎖,也是基于數(shù)據(jù)庫(kù)的鎖機(jī)制實(shí)現(xiàn)。
下面的代碼實(shí)現(xiàn)了對(duì)查詢(xún)記錄的加鎖:
String hqlStr ="from TUser as user where user.name='Erica'";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); // 加鎖
List userList = query.list();// 執(zhí)行查詢(xún),獲取數(shù)據(jù)
query.setLockMode 對(duì)查詢(xún)語(yǔ)句中,特定別名所對(duì)應(yīng)的記錄進(jìn)行加鎖(我們?yōu)門(mén)User 類(lèi)指定了一個(gè)別名 “user” ),這里也就是對(duì)返回的所有 user 記錄進(jìn)行加鎖。
觀察運(yùn)行期 Hibernate 生成的 SQL 語(yǔ)句:
select tuser0_.id as id, tuser0_.name as name, tuser0_.group_id
as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex
from t_user tuser0_ where (tuser0_.name='Erica' ) for update
這里 Hibernate 通過(guò)使用數(shù)據(jù)庫(kù)的 for update 子句實(shí)現(xiàn)了悲觀鎖機(jī)制。
Hibernate 的加鎖模式有:
LockMode.NONE : 無(wú)鎖機(jī)制。
LockMode.WRITE : Hibernate 在 Insert 和 Update 記錄的時(shí)候會(huì)自動(dòng)獲取。
LockMode.READ : Hibernate 在讀取記錄的時(shí)候會(huì)自動(dòng)獲取。
以上這三種鎖機(jī)制一般由 Hibernate 內(nèi)部使用,如 Hibernate 為了保證 Update過(guò)程中對(duì)象不會(huì)被外界修改,會(huì)在 save 方法實(shí)現(xiàn)中自動(dòng)為目標(biāo)對(duì)象加上 WRITE 鎖。
LockMode.UPGRADE :利用數(shù)據(jù)庫(kù)的 for update 子句加鎖。
LockMode. UPGRADE_NOWAIT : Oracle 的特定實(shí)現(xiàn),利用 Oracle 的 for update nowait 子句實(shí)現(xiàn)加鎖。
上面這兩種鎖機(jī)制是我們?cè)趹?yīng)用層較為常用的,加鎖一般通過(guò)以下方法實(shí)現(xiàn):
Criteria.setLockMode
Query.setLockMode
Session.lock
注意,只有在查詢(xún)開(kāi)始之前(也就是 Hiberate 生成 SQL 之前)設(shè)定加鎖,才會(huì)真正通過(guò)數(shù)據(jù)庫(kù)的鎖機(jī)制進(jìn)行加鎖處理,否則,數(shù)據(jù)已經(jīng)通過(guò)不包含 for update 子句的 Select SQL 加載進(jìn)來(lái),所謂數(shù)據(jù)庫(kù)加鎖也就無(wú)從談起。
樂(lè)觀鎖( Optimistic Locking )
相對(duì)悲觀鎖而言,樂(lè)觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫(kù)的鎖機(jī)制實(shí)現(xiàn),以保證操作最大程度的獨(dú)占性。但隨之而來(lái)的就是數(shù)據(jù)庫(kù)性能的大量開(kāi)銷(xiāo),特別是對(duì)長(zhǎng)事務(wù)而言,這樣的開(kāi)銷(xiāo)往往無(wú)法承受。
如一個(gè)金融系統(tǒng),當(dāng)某個(gè)操作員讀取用戶(hù)的數(shù)據(jù),并在讀出的用戶(hù)數(shù)據(jù)的基礎(chǔ)上進(jìn)行修改時(shí)如更改用戶(hù)帳戶(hù)余額),如果采用悲觀鎖機(jī)制,也就意味著整個(gè)操作過(guò)程中(從操作員讀出數(shù)、開(kāi)始修改直至提交修改結(jié)果的全過(guò)程,甚至還包括操作員中途去煮咖啡的時(shí)間),數(shù)據(jù)庫(kù)記錄始終處于加鎖狀態(tài),可以想見(jiàn),如果面對(duì)幾百上千個(gè)并發(fā),這樣的情況將導(dǎo)致怎樣的后果。樂(lè)觀鎖機(jī)制在一定程度上解決了這個(gè)問(wèn)題。樂(lè)觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實(shí)現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),在基于數(shù)據(jù)庫(kù)表的版本解決方案中,一般是通過(guò)為數(shù)據(jù)庫(kù)表增加一個(gè) “version” 字段來(lái)實(shí)現(xiàn)。
讀取出數(shù)據(jù)時(shí),將此版本號(hào)一同讀出,之后更新時(shí),對(duì)此版本號(hào)加一。此時(shí),將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對(duì),如果提交的數(shù)據(jù)版本號(hào)大于數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào),則予以更新,否則認(rèn)為是過(guò)期數(shù)據(jù)。
對(duì)于上面修改用戶(hù)帳戶(hù)信息的例子而言,假設(shè)數(shù)據(jù)庫(kù)中帳戶(hù)信息表中有一個(gè)version 字段,當(dāng)前值為 1 ;而當(dāng)前帳戶(hù)余額字段(balance)為 $100 。
1 操作員 A 此時(shí)將其讀出(version=1),并從其帳戶(hù)余額中扣除 $50($100-$50)。
2 在操作員 A 操作的過(guò)程中,操作員 B 也讀入此用戶(hù)信息(version=1),并從其帳戶(hù)余額中扣除 $20 ($100-$20)。
3 操作員 A 完成了修改工作,將數(shù)據(jù)版本號(hào)加一(version=2),連同帳戶(hù)扣除后余額(balance=$50),提交至數(shù)據(jù)庫(kù)更新,此時(shí)由于提交數(shù)據(jù)版本大于數(shù)據(jù)庫(kù)記錄當(dāng)前版本,數(shù)據(jù)被更新,數(shù)據(jù)庫(kù)記錄 version 更新為 2 。
4 操作員 B 完成了操作,也將版本號(hào)加一(version=2)試圖向數(shù)據(jù)庫(kù)提交數(shù)據(jù)(balance=$80),但此時(shí)比對(duì)數(shù)據(jù)庫(kù)記錄版本時(shí)發(fā)現(xiàn),操作員 B 提交的數(shù)據(jù)版本號(hào)為 2 ,數(shù)據(jù)庫(kù)記錄當(dāng)前版本也為 2 ,不滿(mǎn)足“ 提交版本必須大于記錄當(dāng)前版本才能執(zhí)行更新“ 的樂(lè)觀鎖策略,因此,操作員 B 的提交被駁回。這樣,就避免了操作員 B 用基于 version=1 的舊數(shù)據(jù)修改的結(jié)果覆蓋操作員 A 的操作結(jié)果的可能。
從上面的例子可以看出,樂(lè)觀鎖機(jī)制避免了長(zhǎng)事務(wù)中的數(shù)據(jù)庫(kù)加鎖開(kāi)銷(xiāo)(操作員 A 和操作員 B 操作過(guò)程中,都沒(méi)有對(duì)數(shù)據(jù)庫(kù)數(shù)據(jù)加鎖),大大提升了大并發(fā)量下的系統(tǒng)整體性能表現(xiàn)。需要注意的是,樂(lè)觀鎖機(jī)制往往基于系統(tǒng)中的數(shù)據(jù)存儲(chǔ)邏輯,因此也具備一定的局限性,如在上例中,由于樂(lè)觀鎖機(jī)制是在我們的系統(tǒng)中實(shí)現(xiàn),來(lái)自外部系統(tǒng)的用戶(hù)余額更新操作不受我們系統(tǒng)的控制,因此可能會(huì)造成臟數(shù)據(jù)被更新到數(shù)據(jù)庫(kù)中。在系統(tǒng)設(shè)計(jì)階段,我們應(yīng)該充分考慮到這些情況出現(xiàn)的可能性,并進(jìn)行相應(yīng)調(diào)整(如將樂(lè)觀鎖策略在數(shù)據(jù)庫(kù)存儲(chǔ)過(guò)程中實(shí)現(xiàn),對(duì)外只開(kāi)放基于此存儲(chǔ)過(guò)程的數(shù)據(jù)更新途徑,而不是將數(shù)據(jù)庫(kù)表直接對(duì)外公開(kāi))。
Hibernate 在其數(shù)據(jù)訪問(wèn)引擎中內(nèi)置了樂(lè)觀鎖實(shí)現(xiàn)。如果不用考慮外部系統(tǒng)對(duì)數(shù)據(jù)庫(kù)的更新操作,利用 Hibernate 提供的透明化樂(lè)觀鎖實(shí)現(xiàn),將大大提升我們的生產(chǎn)力。
Hibernate 中可以通過(guò) class 描述符的 optimistic-lock 屬性結(jié)合 version描述符指定。
現(xiàn)在,我們?yōu)橹笆纠械?TUser 加上樂(lè)觀鎖機(jī)制。
1 . 首先為 TUser 的 class 描述符添加 optimistic-lock 屬性:
<hibernate-mapping>
<class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true"
dynamic-insert="true" optimistic-lock="version">
……
</class>
</hibernate-mapping>
optimistic-lock 屬性有如下可選取值:
none:無(wú)樂(lè)觀鎖
version:通過(guò)版本機(jī)制實(shí)現(xiàn)樂(lè)觀鎖
dirty:通過(guò)檢查發(fā)生變動(dòng)過(guò)的屬性實(shí)現(xiàn)樂(lè)觀鎖
all:通過(guò)檢查所有屬性實(shí)現(xiàn)樂(lè)觀鎖
其中通過(guò) version 實(shí)現(xiàn)的樂(lè)觀鎖機(jī)制是 Hibernate 官方推薦的樂(lè)觀鎖實(shí)現(xiàn),同時(shí)也是 Hibernate 中,目前唯一在數(shù)據(jù)對(duì)象脫離 Session 發(fā)生修改的情況下依然有效的鎖機(jī)制。因此,一般情況下,我們都選擇 version 方式作為 Hibernate 樂(lè)觀鎖實(shí)現(xiàn)機(jī)制。
2 . 添加一個(gè) Version 屬性描述符
<hibernate-mapping>
<class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true" dynamic-insert="true"
optimistic-lock="version">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native">
</generator>
</id>
<version column="version" name="version" type="java.lang.Integer"/>
……
</class>
</hibernate-mapping>
注意 version 節(jié)點(diǎn)必須出現(xiàn)在 ID 節(jié)點(diǎn)之后。這里我們聲明了一個(gè) version 屬性,用于存放用戶(hù)的版本信息,保存在 TUser 表的version 字段中。
此時(shí)如果我們嘗試編寫(xiě)一段代碼,更新 TUser 表中記錄數(shù)據(jù),如:
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
List userList = criteria.list();
TUser user =(TUser)userList.get(0);
Transaction tx = session.beginTransaction();
user.setUserType(1); // 更新 UserType 字段
tx.commit();
每次對(duì) TUser 進(jìn)行更新的時(shí)候,我們可以發(fā)現(xiàn),數(shù)據(jù)庫(kù)中的 version 都在遞增。而如果我們嘗試在 tx.commit 之前,啟動(dòng)另外一個(gè) Session ,對(duì)名為 Erica 的用戶(hù)進(jìn)行操作,以模擬并發(fā)更新時(shí)的情形:
Session session= getSession();
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
Session session2 = getSession();
Criteria criteria2 = session2.createCriteria(TUser.class);
criteria2.add(Expression.eq("name","Erica"));
List userList = criteria.list();
List userList2 = criteria2.list();TUser user =(TUser)userList.get(0);
TUser user2 =(TUser)userList2.get(0);
Transaction tx = session.beginTransaction();
Transaction tx2 = session2.beginTransaction();
user2.setUserType(99);
tx2.commit();
user.setUserType(1);
tx.commit();
執(zhí)行以上代碼,代碼將在 tx.commit() 處拋出 StaleObjectStateException 異常,并指出版本檢查失敗,當(dāng)前事務(wù)正在試圖提交一個(gè)過(guò)期數(shù)據(jù)。通過(guò)捕捉這個(gè)異常,我們就可以在樂(lè)觀鎖校驗(yàn)失敗時(shí)進(jìn)行相應(yīng)處理。
轉(zhuǎn)載人員:Nicholas
文章來(lái)源:
http://www.cnblogs.com/obpm/archive/2010/07/11/1775120.html