<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    我的隱式生活(My Implicit Life)

    繼續(xù)搞“對(duì)象”,玩OO.

    首頁(yè) 新隨筆 聯(lián)系 聚合 管理
      11 Posts :: 1 Stories :: 39 Comments :: 0 Trackbacks

    2006年10月15日 #

    近期寫(xiě)了個(gè)電子書(shū)的C/S模式的下載工具,一個(gè)server端,一個(gè)client端。

    目的就是想在公司能很方便的訪問(wèn)家里那些收集很久電子書(shū),方便查閱。

    用了1,2個(gè)星期,雖然寫(xiě)的很爛,但是沒(méi)有用任何第三方的產(chǎn)品(server or db)。

    現(xiàn)在里面的書(shū)籍已經(jīng)接近200本了。

    注:server就用了家里的adsl,所以速度慢,關(guān)閉不定時(shí)。畢竟玩玩嘛。

    有興趣的朋友先裝個(gè)jdk1.5。再運(yùn)行下面壓縮包里的exe文件執(zhí)行即可。

    點(diǎn)此下載

    User ID:???????????????blogjava
    Password:???????????? blogjava
    ?

    posted @ 2006-10-15 13:21 marco 閱讀(3471) | 評(píng)論 (9)編輯 收藏

    2006年9月20日 #

    Java Collection Framwork中的類(lèi)的確是最重要的基礎(chǔ)api,實(shí)現(xiàn)任何算法,基本上都很難離開(kāi)它。

    因此理解這堆“集合(Collection)類(lèi)”很有必要。聲明一下,以前一直都是叫它們集合類(lèi),但是好像Think In Java的作者鄙視了這個(gè)說(shuō)法,嚴(yán)格的說(shuō)應(yīng)該叫Container類(lèi),而后看了它整整一章書(shū)以后,覺(jué)得還是人家說(shuō)的有道理。

    它說(shuō)這個(gè)container類(lèi)庫(kù),包含了兩大類(lèi),Collection和Map,而Collection又可以分為L(zhǎng)ist和Set。當(dāng)然這些抽象概念都被定義成了接口。

    話說(shuō),這樣的分類(lèi)的確是嚴(yán)格按照類(lèi)之間的繼承關(guān)系來(lái)說(shuō)得,但是俺總覺(jué)得很別扭,真動(dòng)手的時(shí)候,還是很難選擇。當(dāng)然,Anytime and Anywhere使用ArrayList絕對(duì)都能解決問(wèn)題,但這樣做畢竟太農(nóng)民了一點(diǎn)。

    所以,我自己有了一些想法。先回歸到最基本最基本的數(shù)據(jù)結(jié)構(gòu)的層面,管你是Collection還是Container,反正描述的都是一堆東西吧。數(shù)據(jù)結(jié)構(gòu)第一章講了一個(gè)結(jié)構(gòu):在物理上連續(xù)分配空間的順序結(jié)構(gòu),叫順序表(希望記性是好的),而離散分配空間的,應(yīng)該叫做鏈表,最常用的就是單鏈表。這兩個(gè)東西,其實(shí)就是很多復(fù)雜數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ),還記得嗎,當(dāng)時(shí)就是講完這些東西,才開(kāi)始講棧、隊(duì)列、二叉樹(shù)、有向無(wú)向圖的。所以,這個(gè)順序結(jié)構(gòu)是很基礎(chǔ)的。而在JAVA中,順序表對(duì)應(yīng)的就是List接口,而一般順序表就是ArrayList(有效進(jìn)行隨機(jī)index查找);而單鏈表就是LinkedList(有效進(jìn)行插入和刪除),兩個(gè)的優(yōu)劣當(dāng)年都講爛了,這里就不說(shuō)了。

    有了這兩個(gè)結(jié)構(gòu)以后,JAVA就不提供Stack和Queue單獨(dú)的類(lèi)了,因?yàn)椋脩?hù)可以用上面兩個(gè)類(lèi)輕易的去實(shí)現(xiàn)。

    那Set和Map有怎么跟List連上關(guān)系呢?

    我認(rèn)為可以把它們看成是無(wú)序和單一的List(Map只是兩個(gè)有映射關(guān)系的List罷了)。

    Set和Map無(wú)序和單一的特性,決定了它們天大的需求就是根據(jù)關(guān)鍵字(元素對(duì)象)檢索。so,為了效率,必須hash。

    有了HashSet和HashMap。

    同時(shí),如果非要保持住元素的順序,有了LinkedHashSet、LinkedHashMap。


    結(jié)論:

    假如你的需求是
    1:往Container中放的對(duì)象是無(wú)序且單一的;
    2:經(jīng)常要檢索。
    用HashSet或HashMap吧。

    ps:這兩個(gè)條件其實(shí)是一回事,因?yàn)槿绻遣粏我坏脑挘闳z索它干嘛。

    如果進(jìn)而需要保持元素的順序,不要讓他順便iteration,那就選擇LinkedHashSet和LinkedHashMap。

    假如你的需求不滿足以上1&2,那你放心,List肯定能幫你解決,你只要稍微想一下是ArrayList好還是LinkedList好。

    題外話:

    關(guān)于Hash,務(wù)必記得要讓自己的元素對(duì)象override hashCode()和 equles() 方法,要不你直接可以洗了睡。

    關(guān)于所有這些Container,務(wù)必記得有個(gè)輔助類(lèi)叫Interator,遍歷盡量要用它。

    關(guān)于一些老的Stack、Vector、HashTable,聽(tīng)說(shuō)以后不要用了哦。收到啦!!

    posted @ 2006-09-20 16:53 marco 閱讀(2323) | 評(píng)論 (0)編輯 收藏

    2006年8月31日 #

    任何信息,基本都是以文字的形式傳播和記錄下來(lái)的。

    在計(jì)算機(jī)中,文字就是字符的集合,也就是字符串,C就是因?yàn)閷?duì)字符串設(shè)計(jì)的不好,才那么容易溢出。而別的一些高級(jí)語(yǔ)言,對(duì)于這個(gè)進(jìn)行了很多的改進(jìn)。

    編程的人由于技術(shù)方向和應(yīng)用方向的不同,日常編程的內(nèi)容差距很大。但是對(duì)于字符串的處理,那可是永遠(yuǎn)都避不開(kāi)的工作。

    昨天跑步的時(shí)候,想了一下,對(duì)于字符串的操作有那么多(search,match,split,replace),感覺(jué)很煩雜,能不能抓住這些操作的一個(gè)基本集?

    不知道對(duì)不對(duì),反正想出來(lái)了一個(gè),這個(gè)基本操作就是search,這里的search的意思是:在輸入串中找到目標(biāo)串的開(kāi)始位置(start index),和結(jié)束位置(end index)。

    有了這個(gè)基本集,別的操作都很好衍生出來(lái):

    局部match:其實(shí)就是要求search操作至少返回一個(gè)start index。

    全match:其實(shí)要求search操作的至少返回一個(gè)start index,并且start index要為零,end index要為輸入串的全長(zhǎng)。

    split:其實(shí)就是search操作之后,把前一個(gè)end index和當(dāng)前的start index之間的字符串截出來(lái)而已。

    replace:其實(shí)就是search操作之后,把start index和end index之間的字符串換成另外的而已。

    所以,歸根到底,都是一個(gè)search操作的拓展罷了。這么一想,感覺(jué)清晰多了。

    這么一來(lái),API對(duì)search的能力支持的好壞和效率高低是衡量字符串操作功能的標(biāo)準(zhǔn),當(dāng)然,如果有直接支持match,split,replace操作的話就更好了。

    java對(duì)字符串search的支持,最基本的就是下面的String的indexOf方法:

    int indexOf(String str)
    ????????? Returns the index within this string of the first occurrence of the specified substring.

    這里我想說(shuō)的是,很多時(shí)候我們所謂要search的目標(biāo)串,根本就不是固定單一的,而是變化多樣的。如果只有一兩種情況,最多用兩次上面的方法唄。但是有些情況是近乎不可能羅列的,例如,我們講的代表email的字符串,我們不可能遍歷它吧。

    所以,需要一種能夠通用表達(dá)字符串格式的語(yǔ)言。這就是Regular Expression(re)。

    假如上面方法indexOf的str參數(shù)能支持re做為參數(shù)的話,那對(duì)于這種多樣的search也可以用上面的方法了。

    可惜,indexOf不支持re作為參數(shù)。

    so,以下就介紹java api中可以用re作為參數(shù)的字符串操作方法(參數(shù)中的regex就是re)。

    --------------------->>
    String類(lèi)的:

    全match操作:
    boolean matches(String regex)
    ????????? Tells whether or not this string matches the given regular expression.

    全replace操作:
    String replaceAll(String regex, String replacement)
    ????????? Replaces each substring of this string that matches the given regular expression with the given replacement.

    首個(gè)replace操作:
    String replaceFirst(String regex, String replacement)
    ????????? Replaces the first substring of this string that matches the given regular expression with the given replacement.

    全split操作:
    String[] split(String regex)
    ????????? Splits this string around matches of the given regular expression.


    有限制數(shù)的split操作:
    String[] split(String regex, int limit)
    ????????? Splits this string around matches of the given regular expression.

    <<---------------------

    可惜啊,可惜,可惜java的String類(lèi)里面沒(méi)有可以支持re的search方法,那如果要用re來(lái)search,只好使用java中專(zhuān)門(mén)的re類(lèi)庫(kù)。

    java中的re類(lèi)庫(kù)主要就兩個(gè)類(lèi),一個(gè)叫Pattern,顧名思義,代表re的類(lèi)。一個(gè)叫Matcher類(lèi),反映當(dāng)前match狀況的類(lèi)(如存放了當(dāng)前search到的位置,匹配的字符串等等信息)。

    一般在構(gòu)造中,“re的表達(dá)式”作為參數(shù)傳遞入Pattern類(lèi),“輸入串(待過(guò)濾串)”作為參數(shù)傳遞入Matcher類(lèi)。

    然后使用Matcher類(lèi)的字符串search方法就可以了。Matcher真正提供search功能的API叫find。下面列出。
    --------------------->>
    Matcher類(lèi)search操作相關(guān)的方法:

    boolean lookingAt()
    ????????? Attempts to match the input sequence, starting at the beginning, against the pattern.

    boolean matches()
    ????????? Attempts to match the entire input sequence against the pattern.

    boolean find()
    ????????? Attempts to find the next subsequence of the input sequence that matches the pattern.

    String group()
    ????????? Returns the input subsequence matched by the previous match.

    <<---------------------

    前三個(gè)都是search方法,返回成功與否。第四個(gè)是返回當(dāng)前search上的字符串。

    ok,至此。使用re的search操作也有眉目了。

    當(dāng)然,Pattern和Matcher也包含直接使用re進(jìn)行的match,split,replace操作。

    --------------------->>
    Patter類(lèi)別的字符串操作方法

    全match操作:
    static boolean matches(String regex, CharSequence input)
    ????????? Compiles the given regular expression and attempts to match the given input against it.

    全split操作:
    String[] split(CharSequence input)
    ????????? Splits the given input sequence around matches of this pattern.

    有限制數(shù)的split操作:
    String[] split(CharSequence input, int limit)
    ????????? Splits the given input sequence around matches of this pattern.


    Matcher類(lèi)別的字符串操作方法

    全replace操作:
    String replaceAll(String replacement)
    ????????? Replaces every subsequence of the input sequence that matches the pattern with the given replacement string.

    首個(gè)replace操作:
    String replaceFirst(String replacement)
    ????????? Replaces the first subsequence of the input sequence that matches the pattern with the given replacement string.

    動(dòng)態(tài)replace(replacement可以根據(jù)被替代的字符串變化而變化)
    Matcher appendReplacement(StringBuffer sb, String replacement)
    ????????? Implements a non-terminal append-and-replace step.

    StringBuffer appendTail(StringBuffer sb)
    ????????? Implements a terminal append-and-replace step.

    <<---------------------

    總結(jié):
    當(dāng)必須使用re的時(shí)候,search操作就要用到Pattern,Matcher,當(dāng)然動(dòng)態(tài)的replace操作也要用到這兩個(gè)類(lèi)。而別的match,replace,split操作,可以使用pattern,Matcher,當(dāng)然也可以直接使用String,推薦還是用回咱們的String吧。

    注:以上都是看jdk1.4以上的文檔得出的結(jié)論,以前版本不能用不負(fù)責(zé)任。

    posted @ 2006-08-31 15:13 marco 閱讀(2692) | 評(píng)論 (0)編輯 收藏

    2006年7月15日 #

    創(chuàng)建和銷(xiāo)毀對(duì)象

    重點(diǎn)關(guān)注對(duì)象的創(chuàng)建和銷(xiāo)毀:什么時(shí)候、如何創(chuàng)建對(duì)象,什么時(shí)候、什么條件下應(yīng)該避免創(chuàng)建對(duì)象,如何保證對(duì)象在合適的方式下被銷(xiāo)毀,如何在銷(xiāo)毀對(duì)象之前操作一些必須的清理行為。

    嘗試用靜態(tài)工廠方法代替構(gòu)造器

    如果一個(gè) client 要實(shí)例化一個(gè)對(duì)象來(lái)使用,傻 b 都知道應(yīng)該先調(diào)用類(lèi)的構(gòu)造器來(lái) new 一個(gè)對(duì)象,之后再調(diào)用相應(yīng)的方法。除了這個(gè)方式, Java Effective 還建議了另一種方法:用靜態(tài)工廠方法來(lái)提供一個(gè)類(lèi)的實(shí)例。以下的例子不反映兩者的優(yōu)劣,只是反映兩者在代碼實(shí)現(xiàn)上的不同,優(yōu)劣之后再談:

    假設(shè)咱們要一個(gè)顏色為黑色、長(zhǎng)度為 50cm 的錘子,自然就用構(gòu)造器創(chuàng)建一個(gè)

    Hammer myHammer =? new Hammer(Color.BLACK, 50);

    而用靜態(tài)工廠方法來(lái)實(shí)例化一個(gè)對(duì)象,如下

    Hammer myHammer = Hammer.factory(Color.BLACK,50);

    也可以用專(zhuān)門(mén)的一個(gè)工廠類(lèi)來(lái)實(shí)例化

    Hammer myHammer = Toolkit.factory(“Hammer”, Color.BLACK,50);??

    單純從上面的代碼上看,真的只有傻 b 才會(huì)選擇靜態(tài)工廠的方法,完全就是多此一舉,直接 new 又快又爽,搞這么麻煩做莫斯(武漢話“什么”的意思)?

    別急,別急,你急個(gè)莫 b (武漢粗話:基本就是“你急個(gè)毛”的意思)?

    下面就說(shuō)說(shuō)用靜態(tài)工廠代替構(gòu)造器的好處( advantage )和不好處( disadvantage )。

    第一個(gè)好處,講你都不信,行家們認(rèn)為,構(gòu)造器有一個(gè)不好的地方就是:這個(gè)方法的簽名( signture )太固定了。

    構(gòu)造器的名字是固定的,生個(gè) Hammer ,構(gòu)造器的名字就是 Hammer (……),唯一能變化的地方就是參數(shù),假設(shè)我的這個(gè)錘子有兩個(gè)很變態(tài)的構(gòu)造需要:

    1 :第一個(gè)參數(shù)是顏色( Color 型),第二個(gè)參數(shù)是錘子頭的重量( int 型)。

    Hammer Color c, int kg {

    //remainder omited

    }

    2 :第一個(gè)參數(shù)是顏色( Color 型),第二個(gè)參數(shù)是錘子的長(zhǎng)度( int 型)。

    Hammer Color c, int cm {

    //remainder omited

    }

    感覺(jué)滿足需要了,但是細(xì)心一看,完了,構(gòu)造器的參數(shù)列表類(lèi)型重復(fù)了,肯定編譯通不過(guò),這是面向?qū)ο髽?gòu)造器天生的缺陷——唯一的變化就是參數(shù),參數(shù)都分辨不了,就真的分辨不了。

    而另外就算參數(shù)能分辨的了,構(gòu)造器一多,它的參數(shù)一多,您根本就不知道每個(gè)參數(shù)是用來(lái)干什么的,只能去查閱文檔,在您已經(jīng)眼花繚亂的時(shí)候再去查文檔,一個(gè)一個(gè)的對(duì),折磨人的活。

    這個(gè)時(shí)候,您就可以考慮用靜態(tài)工廠方法來(lái)實(shí)例化對(duì)象了。因?yàn)殪o態(tài)工廠方法有一個(gè)最簡(jiǎn)單的特點(diǎn)就是:他有可以變化的方法名(構(gòu)造器的名字變不了)。用名字的不同來(lái)代表不同的構(gòu)造需要,這么簡(jiǎn)單的普通的特點(diǎn)在這里就是它相對(duì)于構(gòu)造器的 advantage

    如上面的錘子的例子可以這樣:

    1 Hammer.produceByWeight (Color c, int kg){

    //remainder omited

    }

    2 Hammer.produceByHeight (Color c, int cm){

    //remainder omited

    }

    這是不是一目了然多了。嗯,我是這樣認(rèn)為的。

    第二個(gè)好處,“靜態(tài)工廠方法不需要每次都真的去實(shí)例化一個(gè)對(duì)象”——其實(shí)這也是另一些優(yōu)化方法的前提。

    構(gòu)造器的每次 invoke 必定會(huì)產(chǎn)生一個(gè)新的對(duì)象,而靜態(tài)工廠方法經(jīng)過(guò)一定的控制,完全可以不用每次 invoke 都生成一個(gè)新的對(duì)象。

    為什么不每次都生成一個(gè)對(duì)象的原因就不必說(shuō)了,因?yàn)樵蛱黠@。這個(gè)原因就是為什么要“共享”對(duì)象的原因。

    下面講講通常使用的兩種共享具體策略,也就是具體方法了:

    1 :?jiǎn)卫J降男枰坏┬枰硞€(gè)對(duì)象有單例的需要,必定對(duì)于這類(lèi)對(duì)象的構(gòu)造只能用靜態(tài)工廠方法了。

    2 flyweight 模式和不變( immutable 模式的需要,這兩個(gè)模式很多時(shí)候都說(shuō)一起使用的,一旦一些對(duì)象我們認(rèn)為是不變的,那自然就想拿來(lái)重用,也就說(shuō)共享,而 flyweight 就是用來(lái)重用這些小粒度對(duì)象的。

    Boolean.valueOf (boolean) 方法:

    Boolean a = Boolean.valueOf (100);

    Boolean b = Boolean.valueOf (100);

    ?a,??b兩個(gè)引用都是指向同一個(gè)對(duì)象。

    這些對(duì)象都是不變的,而 valueOf 的控制就是用的 flyweight 方法。

    這種一個(gè)狀態(tài)(如上面一個(gè)數(shù)字)對(duì)應(yīng)的對(duì)象只有一個(gè)還有一個(gè)好處,就是可以直接通過(guò)比較“引用”來(lái)判斷他們是否 equel (這里的 equel 是邏輯相等的意思),以前需要 a.equels(b) ,而一旦用“ flyweight 模式和不變( immutable 模式”后,避免了產(chǎn)生多余的相同對(duì)象,用 a==b 就可以達(dá)到 a.equels(b) 的目的了。這樣當(dāng)然優(yōu)化了 performance ??

    第三個(gè)好處,其實(shí)就是工廠方法的核心好處——我把它稱(chēng)為“抽象類(lèi)型構(gòu)造器”。它可以為我們提供一個(gè)抽象類(lèi)型的實(shí)例,同時(shí)必要的隱藏了抽象類(lèi)型的具體結(jié)構(gòu)。這是 new 怎么都達(dá)不到的。

    這種模式的好處其實(shí)就是面向?qū)ο蟮淖詈诵牡暮锰帲橄蠛途唧w可以分離,一旦抽象定義好了,具體的東西可以慢慢的變化,慢慢的拓展——開(kāi)閉原則。

    Collections Framework API ,都是描述集合類(lèi)型的接口,也就是對(duì)于客戶(hù)端來(lái)看,只有 Collection 這個(gè)類(lèi)要認(rèn)識(shí),而實(shí)際上,實(shí)現(xiàn)這個(gè)接口的 Collection 是多種多樣的。如果要讓用戶(hù)都知道這些具體實(shí)現(xiàn)的 Collection ,就增加了復(fù)雜度。

    這時(shí),通過(guò)一個(gè)靜態(tài)工廠方法,就可以隱藏各種 Collection 的具體實(shí)現(xiàn),而讓 Client 只使用返回的 Collection 對(duì)象就可以了。

    這里還可以加上一些權(quán)限控制,如這些實(shí)現(xiàn)只要對(duì)于工廠來(lái)講是可以訪問(wèn)的,不用是 public 的,而他們只要通過(guò) public 的工廠就可以提供給用戶(hù)。非常有利于代碼的安全。

    靜態(tài)工廠方法的第一個(gè)缺點(diǎn)就是:使用靜態(tài)工廠方法創(chuàng)建的類(lèi)的構(gòu)造器經(jīng)常都是非公共或非 protected 的。 這樣,以后這些類(lèi)就沒(méi)有辦法被繼承了。不過(guò)也有人說(shuō),不用繼承就用 composition 唄。也是!呵呵。

    靜態(tài)工廠方法的第二個(gè)缺點(diǎn)是:在 jdk 文檔里,這些靜態(tài)工廠方法很難跟別的靜態(tài)方法相區(qū)別。 而文檔中,構(gòu)造器是很容易看到的。

    為了一定程度解決這個(gè)問(wèn)題,我們可以用一些比較特別的名字來(lái)給這類(lèi)靜態(tài)工廠方法來(lái)命名。最常用的有:

    valueOf —— 用來(lái)放回跟參數(shù)“相同值”的對(duì)象。

    getInstance —— 返回一個(gè)對(duì)象的實(shí)例。單例模式中,就是返回單例對(duì)象。

    總結(jié):靜態(tài)工廠方法和構(gòu)造器都有各自的特點(diǎn)。最好在考慮用構(gòu)造器之前能先考慮一下靜態(tài)工廠方法,往往,后者更有用一點(diǎn)。如果權(quán)衡了以后也看不出那個(gè)好用一些,那就用構(gòu)造器,畢竟簡(jiǎn)單本分多了。

    posted @ 2006-07-15 12:35 marco 閱讀(625) | 評(píng)論 (0)編輯 收藏

    2006年3月14日 #

         摘要: 關(guān)鍵字:Observer Pattern、Java Thread、Java Swing Application 1 近來(lái)的閱讀 近來(lái)寒暑不常,希自珍慰。武漢天氣不是狂冷,就是狂熱,不時(shí)還給我整個(gè)雪花,就差冰雹了。   自己做的事吧,也沒(méi)有什么勁兒。看看自己喜歡的東西,等著希望中的學(xué)校能給我offers(是復(fù)數(shù)),看著自己想去又不想去的公司的未來(lái)同事在群里面幻想未來(lái)的樣子,別操你大...  閱讀全文
    posted @ 2006-03-14 01:51 marco 閱讀(2603) | 評(píng)論 (2)編輯 收藏

    2006年3月9日 #

    昨天,以前師兄做的系統(tǒng)因?yàn)槁┒从直煌对V,頭讓俺做個(gè)presentation給實(shí)驗(yàn)室下一級(jí)同學(xué)總結(jié)一下,避免以后再犯錯(cuò)。今天講完了就放上來(lái),存?zhèn)€證,以后也好翻閱溝通。

    不涉及主機(jī)安全、網(wǎng)絡(luò)安全、數(shù)據(jù)庫(kù)安全,只從Web應(yīng)用程序的角度,整理歸納一下面臨的主要安全問(wèn)題。

    點(diǎn)擊看大圖。
    r_Web應(yīng)用程序安全問(wèn)題.jpg

    posted @ 2006-03-09 00:12 marco 閱讀(749) | 評(píng)論 (5)編輯 收藏

    2006年2月27日 #

    這幾天瞄了幾本設(shè)計(jì)模式的書(shū),沒(méi)有細(xì)看具體模式啦,而是老是琢磨那些深?yuàn)W無(wú)比的話。這些話經(jīng)常出現(xiàn)在計(jì)算機(jī)的書(shū)籍中,很有禪意,也有哲理。聽(tīng)說(shuō),高手就喜歡寫(xiě)點(diǎn)這樣的話。

    還有就是細(xì)心體味了一下OO的設(shè)計(jì)原則,這些原則是凌駕于模式之上的,也就是更宏觀的原則。

    其中,最高指導(dǎo)的一個(gè)就是“開(kāi)-閉”原則。別的原則,里氏代換原則、依賴(lài)倒置原則、組合/聚合復(fù)用原則和迪米特法則都是為了達(dá)到“開(kāi)-閉”原則而出現(xiàn)的規(guī)則。

    這些原則告訴我很多東西,聚焦于一點(diǎn)就是要“面向抽象”來(lái)做一切事情。

    分析對(duì)象的時(shí)候,要多分析設(shè)計(jì)“抽象”的概念,對(duì)象之間的聯(lián)系要多基于抽象的概念而不是具體,這樣具體才能能夠變化,這樣才是開(kāi)閉。用我自己的話就是要“游走于 抽象”。

    這里有一個(gè)我必須記住的就是,在封裝變化時(shí)候,多用聚合/組合,少用繼承。在封裝原子變化并且是同類(lèi)型對(duì)象時(shí)才用繼承,別的都盡量用聚合/組合。而且盡量不要用多級(jí)繼承,多級(jí)繼承一般意味著有兩種變化脈絡(luò),可能的話,讓兩種變化脈絡(luò)獨(dú)立演化。很明顯,一獨(dú)立演化,又要聚合/組合了。

    還有一個(gè)必須記住的是:運(yùn)用抽象以后,客戶(hù)端的使用發(fā)生了巨大的變化。不再是指那兒用那兒。而是要做更多的準(zhǔn)備工作,因?yàn)檫\(yùn)用抽象,本身就把具體“組合”的職責(zé)推遲到使用的階段。那誰(shuí)使用,肯定是客戶(hù)端。所以,客戶(hù)端的使用要革新。要習(xí)慣用工廠,習(xí)慣把一系列的抽象定具體了,并按照一定方式“組合”起來(lái)用。而且,最終要善于用接口來(lái)調(diào)用方法。

    用小飛推薦的一個(gè)工具畫(huà)了個(gè)圖,如下:
    o_好的OO思想.jpg

                           MARCO ZHANG 2006年2月27日7:18:57

    posted @ 2006-02-27 07:40 marco 閱讀(867) | 評(píng)論 (4)編輯 收藏

    2006年2月23日 #


    “共享”的思想

    共享的idea是生活中最基本的idea,不必有意的使用,到處已經(jīng)存在了。在生活中,大部分事物都是被多人多次使用的,這都是共享的實(shí)際應(yīng)用。

     

    之于OO的共享

     

    OO中的共享,無(wú)非就是說(shuō)讓“對(duì)象”也能被“多人多次”(這里的“人”也無(wú)非就是進(jìn)程、線程而已)使用,更詳細(xì)的說(shuō),就是要讓對(duì)象的生存空間更大一些,生存周期更長(zhǎng)一些。

     

    自己悶個(gè)兒腦子,提煉出了幾個(gè)需要使用共享的環(huán)境(context),也可以說(shuō)是原因吧:

    1.         為了保持“對(duì)象”的一致,我們需要共享。例如,“國(guó)家主席”就一個(gè),不能多了,如果多了,難免決策混亂。

    2.         為了控制“對(duì)象”的存儲(chǔ)空間,我們需要共享。畢竟,目前來(lái)說(shuō),系統(tǒng)的memory還是編程時(shí)最珍貴的資源。

    3.         為了優(yōu)化“對(duì)象”的創(chuàng)建消耗,我們需要共享。如果,一個(gè)對(duì)象的創(chuàng)建過(guò)程消耗太大,系統(tǒng)不能支持頻繁的創(chuàng)建,共享的使用它也是一個(gè)好主意。

    4.         等等。

     

    而在實(shí)際的應(yīng)用中,往往我并沒(méi)有細(xì)想“我為什么使用共享?”,已經(jīng)不自覺(jué)的就用了;如果真的認(rèn)真分析起來(lái),基于的環(huán)境也是多樣,并不會(huì)只是上面的其中一種。

     

    常用的“共享”方法或模式(我曾經(jīng)用過(guò)的,知道的不多,望諒解):

    1.         Singleton模式”:一個(gè)class就一個(gè)對(duì)象實(shí)例,大家都用它,滿足context1

    2.         pool技術(shù)”:只提供一定數(shù)目的對(duì)象,大家都用他們,實(shí)現(xiàn)context2context3

    3.         flyweight模式”:一個(gè)class的一個(gè)狀態(tài)就一個(gè)對(duì)象實(shí)例,實(shí)現(xiàn)一個(gè)狀態(tài)對(duì)象的共享,實(shí)現(xiàn)context2context3

     

    使用時(shí)要注意的地方:

    1.         確定共享的scope。例如,在Java Web Application中就是選擇是pagesession還是application,當(dāng)然也可以是jvm級(jí)別的static

    2.         確認(rèn)thread safe。當(dāng)共享的對(duì)象可能被多個(gè)線程共享時(shí),這是不可以回避的問(wèn)題。

    3.         應(yīng)對(duì)對(duì)象狀態(tài)的變化。一旦共享的對(duì)象發(fā)生了變化,我們?cè)趺刺幚恚扛淖冎釛壷恳彩俏覀冃枰_定的。

     

    項(xiàng)目中的應(yīng)用:

     

    項(xiàng)目需求:

    為學(xué)校的同學(xué)提供Web查詢(xún),查詢(xún)的內(nèi)容有很多。其中,“查課表”、“查考表”是最為關(guān)鍵的需求,以后可能還要提供“查詢(xún)空閑自習(xí)教室”的功能。

    在這些查詢(xún)中,有一個(gè)共同點(diǎn),就是都涉及“教室”這一對(duì)象。“查課表”時(shí)要告訴同學(xué)在哪個(gè)教室上課,“查考表”時(shí)要告訴同學(xué)在哪個(gè)教室考試,等等。

     

    數(shù)據(jù)庫(kù)設(shè)計(jì):

    對(duì)于“查課表”用例,有關(guān)的數(shù)據(jù)庫(kù)設(shè)計(jì)如下:
    o_5-1-1.jpg 


    對(duì)象層的設(shè)計(jì):

    o_5-1.JPG
     

    學(xué)生每查詢(xún)一門(mén)課程的課表,系統(tǒng)就會(huì)sql查詢(xún)“視圖V_LESSONSCHEDULE”,進(jìn)而生成一個(gè)LessonSchedule對(duì)象,然后返回給用戶(hù)顯示。當(dāng)然,在生成這個(gè)LessonSchedule對(duì)象的過(guò)程中,屬于它的Classroom對(duì)象,以及更深一步的BuildingArea對(duì)象都會(huì)生成。下面就是這個(gè)過(guò)程的順序圖:

     

    o_5-2.JPG 


    因此,每生成一個(gè)“課表”對(duì)象(
    LessonSchedule)或“考表”對(duì)象(ExamSchedule)時(shí),都要:

    1.         查數(shù)據(jù)庫(kù)中的教室、教學(xué)樓、校區(qū)的信息;

    2.         創(chuàng)建相應(yīng)的“教室對(duì)象”(包括了屬于它的“教學(xué)樓”對(duì)象和“校區(qū)”對(duì)象)。

     

    考慮共享“教室”對(duì)象

    “教室”對(duì)象一旦可以生成以后,完全可以給后續(xù)共享使用,不必要每個(gè)查詢(xún)都要去生成。

     

    詳細(xì)說(shuō)是基于下面的考慮:

    1.         這類(lèi)查詢(xún)用例(查課表,查考表)發(fā)生的頻繁很高很高,也就是說(shuō),一旦讓用戶(hù)查起來(lái),系統(tǒng)中會(huì)產(chǎn)生大量的“教室”對(duì)象這類(lèi)對(duì)象,應(yīng)該說(shuō)會(huì)占很大的內(nèi)存空間。

    2.         共享“教室”對(duì)象后,可以減少對(duì)數(shù)據(jù)庫(kù)的查詢(xún)次數(shù),并降低了查詢(xún)粒度(以前是基于二級(jí)視圖查詢(xún),現(xiàn)在可以基于基本表查詢(xún)),提高了一點(diǎn)數(shù)據(jù)庫(kù)查詢(xún)性能。

     

    當(dāng)然,同時(shí)我腦袋中也有反對(duì)的聲音:

    1.         雖說(shuō),這類(lèi)查詢(xún)會(huì)產(chǎn)生很多相同的“教室”對(duì)象,但是JVM本生提供的垃圾回收功能完全可以處理它。除非,“同時(shí)”有很多很多這類(lèi)對(duì)象都在被使用,根本回收不了,才會(huì)造成內(nèi)存短缺。

    2.         如果,我以某種共享機(jī)制讓這些“教室”對(duì)象,在系統(tǒng)中存在下來(lái)(延長(zhǎng)了生命周期)了。而它們本身的數(shù)目就很多(如,我們學(xué)校就有××),可能還沒(méi)有等你用上,系統(tǒng)已經(jīng)掛了。另外,如果不是同時(shí)有很多查詢(xún)需要,我留這么多“教室”對(duì)象在系統(tǒng)里,反而是占了內(nèi)存,而不是優(yōu)化了內(nèi)存的使用。

    1.         所有模式的通病――“增加了復(fù)雜度”。

     

    結(jié)論:

    經(jīng)過(guò)我們分析,系統(tǒng)對(duì)于“教室”對(duì)象的重復(fù)使用,頻繁程度非常高。一般,有10個(gè)人同時(shí)使用,就有5個(gè)人左右涉及的用例要使用“教室”對(duì)象。把它共享起來(lái),還是有一定必要的。

     

    進(jìn)一步考慮:

    而實(shí)際上,我們可以進(jìn)一步共享下去:

    除了讓客戶(hù)端共享“教室對(duì)象(Classroom)”外,還可以讓“教室對(duì)象”共享“教學(xué)樓對(duì)象(Building)”,和讓“教學(xué)樓對(duì)象”共享“校區(qū)對(duì)象(Area)”。

    因此,最終的共享是在三級(jí)上都實(shí)現(xiàn)。

     

    Flyweight模式:

     

    特點(diǎn):

    書(shū)上說(shuō):“享元模式可以使系統(tǒng)中的大量小粒度對(duì)象被共享使用”。第一,對(duì)象出現(xiàn)的量要大,想必這比較好理解,很少使用的也就沒(méi)有必要共享了;第二,要小粒度,我比較納悶?難道對(duì)于大粒度對(duì)象就不行嗎?可能書(shū)上認(rèn)為,大粒度對(duì)象的共享已經(jīng)占了比較大的空間,沒(méi)有小對(duì)象那么有效吧。

     

    另外,書(shū)上還說(shuō),要使用“享元模式”,被共享的對(duì)象的狀態(tài)(類(lèi)別)要比較固定,這樣就可以為每一個(gè)狀態(tài)僅僅創(chuàng)建一個(gè)對(duì)象。當(dāng)然,如果每次使用對(duì)象時(shí),對(duì)象的狀態(tài)都是不一樣的,那就根本不存在共享這些對(duì)象的必要了。

     

    聯(lián)系項(xiàng)目思考:

    基于上面對(duì)項(xiàng)目的分析,“教室”、“教學(xué)樓”、“校區(qū)”對(duì)象都是在系統(tǒng)中會(huì)被大量使用的對(duì)象,而且粒度的確比較小;并且它們有固定的類(lèi)別,而且不易改變。如校區(qū)對(duì)象,暫時(shí)就有4個(gè)。教學(xué)樓可能4050個(gè)左右。很適合“享元模式”的使用環(huán)境。

     

    確定共享方式:

    1.         確定共享對(duì)象的scope。在本web程序中,這些共享對(duì)象的scope理應(yīng)是application,而更簡(jiǎn)單的一個(gè)作法就是把這些對(duì)象設(shè)為static,我選擇后者。

    2.         確認(rèn)thread safe。這些對(duì)象是可能被多個(gè)servlet訪問(wèn)的,也就是有可能存在多線程訪問(wèn)。但是,由于這些對(duì)象的可變性很差,一旦創(chuàng)建就不大可能變化。因此,我決定把這寫(xiě)共享對(duì)象設(shè)計(jì)成不變模式的,一旦創(chuàng)建就只會(huì)被讀取,而不會(huì)改寫(xiě),這樣就不存在多線程控制的問(wèn)題了。

    3.         應(yīng)對(duì)對(duì)象狀態(tài)的變化,如某個(gè)教室的類(lèi)型變了。這里采取的是舍棄的方法,為每個(gè)工廠添加了一個(gè)清空方法――clear(),用于清空已經(jīng)生成的共享對(duì)象。

     

    設(shè)計(jì)類(lèi)圖:

    o_5-3.JPG 

    當(dāng)然,也可以把這些工廠都設(shè)計(jì)成“Singleton模式”的,使它們只會(huì)有一個(gè)實(shí)例。

     

    客戶(hù)端使用:

    由于共享的對(duì)象都被包含在了“課表”和“考表”對(duì)象里,不會(huì)被客戶(hù)端直接訪問(wèn),因而不會(huì)對(duì)客戶(hù)端的使用有任何影響:

     

    實(shí)例代碼

     
     1//取得編號(hào)為32號(hào)課程的“課表對(duì)象”
     2
     3LessonSchedule oneLesson = LessonSchedule.findByLessonNum(32);
     4
     5 
     6
     7//獲得教室對(duì)象
     8
     9Classroom oneClassroom = oneLesson.getLnkClassroom();
    10
    11 
    12
    13//獲得教學(xué)樓對(duì)象
    14
    15Building oneBuilding = oneClassroom.getLnkBuilding();
    16
    17 
    18
    19//獲得校區(qū)對(duì)象
    20
    21Area oneArea = oneBuilding. getLnkArea();
    22
    23 
    24
    25//再次重新生成一個(gè)編號(hào)為32號(hào)的“課表對(duì)象”
    26
    27LessonSchedule twoLesson = LessonSchedule.findByLessonNum(32);
    28
    29 
    30
    31//獲得教室對(duì)象
    32
    33Classroom twoClassroom = twoLesson.getLnkClassroom();
    34
    35 
    36
    37//獲得教學(xué)樓對(duì)象
    38
    39Building twoBuilding = twoClassroom.getLnkBuilding();
    40
    41 
    42
    43//獲得校區(qū)對(duì)象
    44
    45Area twoArea = twoBuilding. getLnkArea();
    46

    oneClassroomtwoClassroomoneBuildingtwoBuildingoneAreatwoArea由于都是32號(hào)課程的東西,根據(jù)我們的設(shè)計(jì)意圖,應(yīng)該實(shí)現(xiàn)共享。

    而實(shí)際上,它們每對(duì)的確是同一個(gè)對(duì)象的引用。因此,實(shí)現(xiàn)了預(yù)期的設(shè)想。

     

    Review

    在本項(xiàng)目中,當(dāng)?shù)谝淮卧O(shè)計(jì)出來(lái)的時(shí)候,我們發(fā)現(xiàn)了某些對(duì)象恰好有共享的需要。

     

    而更多的實(shí)際情況是,這些需要共享的“信息或狀態(tài)”在設(shè)計(jì)中并不是那么恰好的表現(xiàn)為“一個(gè)對(duì)象”的粒度,而是要不就包含在一個(gè)對(duì)象內(nèi)部,要不就跨幾個(gè)對(duì)象。在這樣的情況下,共享的設(shè)計(jì)更多是發(fā)生在代碼重構(gòu)階段而不是第一的設(shè)計(jì)階段。當(dāng)然,為了共享對(duì)象而做出的代碼重構(gòu),最重要的一步就是把需要共享的“信息或狀態(tài)”設(shè)計(jì)成為新的對(duì)象。

     

    對(duì)于,“享元模式”來(lái)說(shuō),就是要把需要共享的“信息或狀態(tài)”設(shè)計(jì)成“享元對(duì)象”。別的在此就不說(shuō)了,因?yàn)槲乙膊欢耍呛恰?/SPAN>


                                              MARCO ZHANG 2006年2月23日13:48:49

    posted @ 2006-02-23 14:14 marco 閱讀(1284) | 評(píng)論 (4)編輯 收藏

    2006年2月18日 #

    廢話:

    預(yù)料中的日志3暫時(shí)生產(chǎn)不出來(lái),話說(shuō)難產(chǎn)就好,別夭折就行,有點(diǎn)掉價(jià)哦,呵呵。

     

    因?yàn)橛行〇|西還沒(méi)有想清楚。那就先搞個(gè)四吧,這個(gè)東西還是清楚那么一點(diǎn)的。

    一版描述:

    項(xiàng)目需求

    使每個(gè)servlet能對(duì)用戶(hù)訪問(wèn)權(quán)限進(jìn)行檢查。簡(jiǎn)單來(lái)說(shuō),就是要給每個(gè)servlet加個(gè)鎖,有鑰匙的用戶(hù)才能訪問(wèn)。

     

    而項(xiàng)目中用戶(hù)所謂的訪問(wèn)權(quán)限是基于他擁有的角色。也就是說(shuō),servlet對(duì)用戶(hù)訪問(wèn)權(quán)限的檢查,就是對(duì)他所擁有角色的檢查。暫時(shí),每個(gè)用戶(hù)只能擁有一個(gè)角色。

     

    項(xiàng)目的角色很多,但是在web端暫時(shí)只有如下的三種:

     

    項(xiàng)目暫時(shí)角色

    游客,學(xué)生,教師

     

    既然這樣,servlet的加鎖方式就有:

     

    servlet加鎖方式

     游客,學(xué)生,教師,
    游客或?qū)W生,游客或教師,學(xué)生或教師,
    游客或?qū)W生或教師

     注:上面的“游客”就是“游客角色有權(quán)訪問(wèn)”的意思,依此類(lèi)推。

     

    這里只有關(guān)系“或”(||),如果一個(gè)servlet的加鎖方式是“學(xué)生或教師”,也就是說(shuō)擁有學(xué)生或教師角色的用戶(hù)都可以訪問(wèn)它。關(guān)系“與”(&&)在這里不太可能存在,因?yàn)闆](méi)有需求說(shuō):某個(gè)servlet一定只能由“既是學(xué)生又是教師的用戶(hù)”才能訪問(wèn),而且前面也說(shuō)了,暫時(shí)一個(gè)用戶(hù)“有且只有”一個(gè)角色。

     

    So we can get following function


    “加鎖方式數(shù)” = 2的“角色數(shù)”次方 - 1


    “加鎖方式數(shù)”是關(guān)于“角色數(shù)”的指數(shù)函數(shù),也就是說(shuō)它是關(guān)于“角色數(shù)”成“指數(shù)級(jí)”增長(zhǎng)的,應(yīng)該說(shuō)很快了吧。


    3
    個(gè)角色就有23次方-1個(gè),也就是7個(gè)加鎖方式。

     

    運(yùn)用OO的最基本方式,就是封裝對(duì)象。既然有為servlet“看門(mén)”的責(zé)任,那就把這個(gè)責(zé)任封裝成一個(gè)對(duì)象,用個(gè)俗名:validator

     

    接著,運(yùn)用共性和個(gè)性的分析方法,既然有那么多種不同的看門(mén)方式(加鎖方式),那就搞一個(gè)接口,然后讓各種“不同”都實(shí)現(xiàn)這個(gè)接口,形成子類(lèi)。那就有下面的圖:

    o_4-1.JPG

    可以看到,由于有7個(gè)加鎖方式,那就要有7個(gè)子類(lèi)。每個(gè)子類(lèi)根據(jù)自己邏輯override接口的validate方法。

     

    這樣,對(duì)于一個(gè)servlet,想讓它上什么樣的鎖,只要讓它拿到對(duì)應(yīng)的子類(lèi)的引用即可,如下圖中的ClientServlet,我們規(guī)定只能有“學(xué)生或教師”才能訪問(wèn)它。它的部分代碼便是:


    o_4-2.JPG

     

     

    1//new對(duì)應(yīng)的Validator接口的子類(lèi)。
    2//這里是學(xué)生或教師可訪問(wèn),因此要new Student_Or_Teacher_Validator
    3Validator validator = new Student_Validator();
    4//然后調(diào)用驗(yàn)證方法就可以了
    5boolean ok = validator.validate();

    至此,第一個(gè)解決方案就出來(lái)了。

    思考:

    不足

    validator接口的子類(lèi)數(shù)目隨“角色數(shù)”成“指數(shù)級(jí)”增長(zhǎng),數(shù)量太多;而且子類(lèi)中重復(fù)邏輯的代碼很多,如“Student_Or_Teacher_Validator”就重復(fù)了“Student_Validator”和“Teacher_Validator”的邏輯,萬(wàn)一“Student_Validator”的邏輯要改,只要涉及Student的子類(lèi)都要跟著改,維護(hù)上不方便。

     

    進(jìn)一步改進(jìn)的可能

    Student_Or_Teacher_Validator類(lèi)”的validate方法很大程度上就是“Student_Validator類(lèi)”的validate方法和“Teacher_Validator類(lèi)”的validate方法“或操作”出來(lái)的結(jié)果。

     

    因此,能不能考慮由“Student_Validator類(lèi)的validate方法”和“Teacher_Validatorvalidate方法”動(dòng)態(tài)的構(gòu)造一個(gè)功能如“Student_Or_Teacher_Validator類(lèi)的validate方法”。

     

    這樣,“Student_Or_Teacher_Validator”就可以省略了,只剩下一些原子的“角色類(lèi)”。要實(shí)現(xiàn)Student_Or_Teacher_Validator的驗(yàn)證功能,拿Student_ValidatorTeacher_Validator裝配一下就可以了。

     

    結(jié)論,需要根據(jù)實(shí)際情況,動(dòng)態(tài)的改變(裝配)“Validator接口對(duì)象”的validate方法。

     

    第一個(gè)火花就是“裝飾模式”。它可以讓客戶(hù)端動(dòng)態(tài)的組裝對(duì)象的方法。真神奇!

    第二版來(lái)啦

    o_4.JPG

     

    注:上圖中出現(xiàn)了AndRelationAnd的一系列角色類(lèi),可以暫時(shí)省略不看,因?yàn)榍懊嬲f(shuō)了,現(xiàn)在還沒(méi)有“與”關(guān)系這個(gè)需求。只看Or的就可以。

     

    我喜歡叫這個(gè)模式為洋蔥模式,一層包一層,最外層對(duì)象某方法的邏輯是由內(nèi)部一層一層對(duì)象的同一方法組合出來(lái)的。

     

    使用了這個(gè)模式,便不用如一版那樣實(shí)現(xiàn)那么多子類(lèi),只要實(shí)現(xiàn)幾個(gè)“角色類(lèi)”即可,這里有3個(gè)(學(xué)生角色:Or_Student、教師角色:Or_Teacher、游客角色:Or_Guest)。所有一版中別的子類(lèi)都可以由這3個(gè)組裝出來(lái)。

     

    如要生成一版中的Student_Or_Teacher_Validator對(duì)象,可以用Or_StudentOr_Teacher兩個(gè)對(duì)象“Or”出來(lái):

     

    1Validator validator = new Or_Student(new Or_Teacher(OrRelation(req)));

     

    如要生成一版中的Guest_Or_Student_Or_Teacher_Validator對(duì)象,可以用Or_StudentOr_TeacherOr _Guest三個(gè)對(duì)象“Or”出來(lái):

     

    1Validator validator = new Or_Student(new Or_Teacher(new Or_Guest(OrRelation(req))));

     

     

    這種一層包一層的new方式,是不是很像洋蔥?第一次看是很不習(xí)慣,看多了就覺(jué)得習(xí)慣了。

    對(duì)客戶(hù)端的影響:

    一版中,客戶(hù)端要什么樣的驗(yàn)證類(lèi),就直接使用具體類(lèi)。

    二版中,客戶(hù)端要什么樣的驗(yàn)證類(lèi),它的工作多了那么一丁點(diǎn),它需要先組裝一下,正如上面的例子。這種組裝的方法很易于理解和使用,不會(huì)給客戶(hù)端帶來(lái)任何的不便。如果實(shí)在覺(jué)得客戶(hù)端組裝不出來(lái)(傻B客戶(hù)端),也可以搞個(gè)工廠給它supply

    優(yōu)點(diǎn):

    相對(duì)一版最明顯的優(yōu)點(diǎn)就是類(lèi)的數(shù)目少了很多。

     

    一版不是說(shuō)“指數(shù)級(jí)”嗎?這里只是線性的了。假設(shè)某一天系統(tǒng)拓展到有10個(gè)角色,一版就有210次方那么多個(gè),也就是1024個(gè)驗(yàn)證類(lèi)。

     

    而二版還是10個(gè)角色類(lèi),別的都可以在客戶(hù)端使用的時(shí)候,動(dòng)態(tài)的組裝

     

    更重要的是代碼結(jié)構(gòu)好了,重復(fù)邏輯少了。每個(gè)邏輯都以最atomic的大小放到最應(yīng)該的地方。

     

    進(jìn)而,維護(hù)的代價(jià)少多了。如某天“教師角色”的驗(yàn)證邏輯發(fā)生了變化,只要改動(dòng)Or_Teacher一個(gè)地方即可。

                     MARCO ZHANG 2006年2月18日23:49:56

    posted @ 2006-02-18 23:53 marco 閱讀(1248) | 評(píng)論 (5)編輯 收藏

    2006年2月16日 #

        說(shuō)起這個(gè)工廠模式,一時(shí)還真不知道該如何說(shuō)起。反正這是我的開(kāi)發(fā)日志,不提理論的東西,理論的東西那里都有,我只想把具體實(shí)踐記錄下來(lái)給師弟師妹們作個(gè)參考,積累點(diǎn)經(jīng)驗(yàn)。所有這些文字都是集中講一點(diǎn)――“在什么情況下為什么用某種模式好,為什么好,為什么在那種情況下能想起來(lái)用?”。

           研究生院項(xiàng)目中“明顯”使用了“工廠方法模式”。其實(shí)在遇到具體問(wèn)題的時(shí)候,即使我們不知道有這個(gè)模式存在,我們也肯定會(huì)造一個(gè)類(lèi)似的東西出來(lái)。但是,肯定沒(méi)有書(shū)上論述的那么好,那么全面。我想這就是看書(shū)的好處吧。

     

    工廠方法出現(xiàn)的必然(我的理解,一個(gè)很狹隘并幼稚理的人的理解)

     

           剛開(kāi)始使用這個(gè)東西的時(shí)候,只是感覺(jué)是單純的一種模式,用于創(chuàng)建需要的對(duì)象。

           但是隨著使用和思考的深入,越發(fā)發(fā)現(xiàn)它給我的啟示不只在于單純的對(duì)象創(chuàng)建,而是告訴我應(yīng)該怎么理解“產(chǎn)品”,怎么得到“產(chǎn)品”,怎么消費(fèi)“產(chǎn)品”,以至于以后怎么設(shè)計(jì)“產(chǎn)品”。

           下面這個(gè)線索是我對(duì)它出現(xiàn)必然性的理解:

    1.         “針對(duì)接口編程”

      這是OO世界中經(jīng)典的規(guī)范,不管你主動(dòng)還是被動(dòng),你天天都在用這個(gè)東西。

        接口是共性的表示,在對(duì)象的世界中,共性和個(gè)性的辯證關(guān)系是最重要的關(guān)系。在萬(wàn)千的對(duì)象中,通過(guò)它們之間的共性和個(gè)性,可以形成最基本對(duì)象層級(jí)架構(gòu)。

    假設(shè)我們的討論域中有以下一些對(duì)象:“學(xué)生”、“大學(xué)生”、“小學(xué)生”、“中學(xué)生”;我們不用細(xì)想,學(xué)過(guò)一天OO的人都可以為這些耳熟能詳?shù)膶?duì)象們,通過(guò)個(gè)性和共性的關(guān)系得出下面的結(jié)構(gòu)圖。

    o_1.JPG

     

           把這些對(duì)象之間的關(guān)系定義成這樣是順理成章的。

           下一步肯定是讓客戶(hù)端“使用”這個(gè)接口啦。也就是如下圖:

     

    o_2.JPG

    2.         接口和具體類(lèi)的矛盾

    勿庸置疑,我們只希望Client跟接口Student打交道,讓它根本就不知道Student有哪些子類(lèi),絕對(duì)不希望直接跟它們打交道。

    但這里出現(xiàn)的困難是,接口都是“假”的,都是由具體類(lèi)upcast的。

    如果Client要使用接口StudentClient中必須會(huì)出現(xiàn)下面的代碼:

    Student marco = new Small_Student();

           只要一出現(xiàn)這個(gè)代碼,就說(shuō)明Client不只跟Student打交道了,它知道了Small_Student類(lèi),這違反了我們預(yù)先的想法。

    3.         找“人”幫我去創(chuàng)建“接口對(duì)象”

        從上圖體現(xiàn)出來(lái)的結(jié)構(gòu)看,Client只想跟Student打交道的目的是實(shí)現(xiàn)不了的了。

           最簡(jiǎn)單的方法就是找另外的“幫手”去幫我生成這個(gè)“接口對(duì)象”。這個(gè)幫手它知道“接口對(duì)象”的具體類(lèi)型,但是它為客戶(hù)端提供的卻一定是“接口類(lèi)型”。這就符合我們的要求了。如圖:

    o_3.JPG

        這樣,Client就可以既用到了“Student接口對(duì)象”,又不用因?yàn)椤爸挥芯唧w類(lèi)才能創(chuàng)建對(duì)象”的規(guī)則,而必須對(duì)其子類(lèi)結(jié)構(gòu)有完全的了解。它成功的解決了2中的矛盾。

           而“負(fù)責(zé)創(chuàng)建具體類(lèi)對(duì)象的任務(wù)”全部都落在了這個(gè)“幫手”身上,這個(gè)“幫手”(Student_Factory)就是工廠模式中的工廠類(lèi),更具體的說(shuō),它就是“簡(jiǎn)單工廠模式”中的“簡(jiǎn)單工廠類(lèi)”。

           我覺(jué)得,即使一點(diǎn)都不知道工廠模式,一旦我遇到了2里說(shuō)的矛盾,我也會(huì)用這樣的方法處理。

    4.         這個(gè)“幫手”不符合“開(kāi)-閉原則”

    這個(gè)幫手的確不錯(cuò)了,而且一躍成為系統(tǒng)中最重要的對(duì)象了,所有“創(chuàng)建具體類(lèi)的邏輯”都放進(jìn)去了,也就是因?yàn)橹匾f(wàn)一掛了不就慘了。

    再者,它不符合“開(kāi)-閉”原則,我不能在不修改這個(gè)幫手的情況下添加任何一個(gè)產(chǎn)品。在這個(gè)例子中就是,如果那天我有病非要加一個(gè)“幼兒園學(xué)生”進(jìn)來(lái),那您就必須修改這個(gè)“幫手”的代碼了,這個(gè)“幫手”現(xiàn)在就變成Version2(如下圖)了,這個(gè)二版的幫手,可以在“代碼”層實(shí)現(xiàn)對(duì)一版(還沒(méi)有添加幼兒園學(xué)生之前)的通用,但這種保證在“開(kāi)-閉”原則看來(lái),還是不夠的,不保險(xiǎn)的,它要的是在類(lèi)的結(jié)構(gòu)上的保證。聲明一下,這是我很感性的理解,不正確的可能性很高。

    o_4.JPG

    5.         讓“幫手”也多態(tài)

    這里可以嘗試讓“幫手”也多態(tài)一下,這樣“每種學(xué)生類(lèi)創(chuàng)建的任務(wù)”都被分派到了多態(tài)出來(lái)的類(lèi)中去了。這時(shí),再有新的學(xué)生類(lèi)型加進(jìn)來(lái),添加一個(gè)對(duì)應(yīng)的幫手就可以了。這樣雖然類(lèi)多了一些,但是符合“開(kāi)-閉”原則,書(shū)上稱(chēng)之為“工廠方法模式”。如圖:

    o_5.JPG

        假如Client現(xiàn)在要使用一個(gè)小學(xué)生,代碼如下:

    1        //創(chuàng)建一個(gè)小學(xué)生工廠類(lèi)這個(gè)幫手
    2        Student_Factory factory = new Small_Student_Factory();
    3        //求助這個(gè)幫手,幫我創(chuàng)建一個(gè)

    4        Student primaryStudent = factory.produce();
    5        //這時(shí)就可以使用這個(gè)小學(xué)生了,讓它玩玩游戲吧

    6        primaryStudent.playGames();
    7

        在這里還是要強(qiáng)調(diào)兩點(diǎn):

    n         雖然實(shí)際上Client的確使用了一個(gè)小學(xué)生對(duì)象(Small_Student),但這里Client也認(rèn)為它就是Student對(duì)象,這里一定要用Student接口來(lái)隱藏它的具體類(lèi)。

    n         另外,卻不需要用Student_Factory這個(gè)接口來(lái)隱藏它的具體類(lèi),因?yàn)椋?/SPAN>Client實(shí)際就是通過(guò)“選擇它的具體類(lèi)”這招兒來(lái)“選擇創(chuàng)建的學(xué)生類(lèi)型”。這里的Student_Factory更多的功能不是“隱藏”具體類(lèi),而是“規(guī)范”具體類(lèi)。

     

    項(xiàng)目實(shí)踐

     

           扯淡到此,該聯(lián)系我們的項(xiàng)目啦。

           由于是做研究生院的項(xiàng)目,其中巨大的需求就是要讓同學(xué)能在網(wǎng)上提交各種申請(qǐng)單,申請(qǐng)退學(xué)的,申請(qǐng)轉(zhuǎn)專(zhuān)業(yè)的,申請(qǐng)復(fù)學(xué)的,申請(qǐng)保留學(xué)籍的,除了申請(qǐng)女朋友的外,應(yīng)有盡有。       對(duì)于這些單子,用最最基本OO思維,根據(jù)共性個(gè)性分析方式,抽象出一個(gè)申請(qǐng)單接口,和若干的具體類(lèi)。

           當(dāng)然,除了概念上感性上吻合以外,在項(xiàng)目中它們也要“真”的有巨大的共性才行,如都要提交,修改,刪除,審核,打印等功能。

           靠,既然都這樣了,肯定就用一接口規(guī)定它了。

           想到這里,加上有了上面的思考,不用說(shuō)了,就用工廠方法模式啦。

     

    o_6.JPG

        圖中Ent_Shift就是申請(qǐng)單接口。等于前面分析的Student接口。還可以看到有很多具體類(lèi),前面例子中是代表各種學(xué)生,這里就是代表各種申請(qǐng)單。

           當(dāng)然,這里還有很多工廠,也就是前面一直叫的“幫手”。

    o_7.JPG

           Bean_Shift就是工廠接口,相當(dāng)于前面的Student_Factory接口。還有很多的具體類(lèi)就是生產(chǎn)各種申請(qǐng)單的工廠類(lèi)。

           下面就是使用“申請(qǐng)單工廠方法模式”的一段客戶(hù)端代碼:

     1        //聲明申請(qǐng)單接口
     2        Ent_Shift es = null;
     3        //聲明申請(qǐng)單工廠接口

     4        Bean_Shift bs = null;
     5        //根據(jù)傳入的申請(qǐng)單類(lèi)型數(shù)字身成對(duì)應(yīng)的申請(qǐng)單工廠

     6        switch (shiftTypeID) {
     7            case
     Bean_Shift.SHIFT_CHANGETEAAP :
     8                bs = new
     Bean_Shift_CHANGETEAAP();
     9                break
    ;
    10            case
     Bean_Shift.SHIFT_RESERVEAP :
    11                bs = new
     Bean_Shift_RESERVEAP();
    12                break
    ;
    13            case
     Bean_Shift.SHIFT_RENEWAP :
    14                bs = new
     Bean_Shift_RENEWAP();
    15                break
    ;
    16             //省略了別的申請(qǐng)單……………………

    17            default :
    18                this.forwardErr(req, resp, "所選擇的異動(dòng)類(lèi)別不存在"
    );
    19        }

    20
    21        try 
    {
    22            //調(diào)用工廠接口的生產(chǎn)方法

    23            es = bs.getBlankShift(stuID);
    24
                
    25        }
     catch (Exception e) {
    26            this
    .forwardErr(req, resp, DB_ERROR);
    27        }

    28        //調(diào)用單子的提交方法
    29        es.submit(req);
    30
            
    31        //發(fā)給頁(yè)面去顯示

    32        es.fowrardWithSessionObject(
    33
                    req,
    34
                    resp,
    35
                    Ent_Shift.nameInSessionAndRequest);
    36

     

    升華

     

           個(gè)人比較同意《Design Pattern Explained》中作者講的,要用好很多的模式,其中都有一個(gè)思路,就是用接口或抽象類(lèi)來(lái)隱藏子類(lèi)的不同。

           我每當(dāng)看到這時(shí),老是會(huì)被一種思路困擾――“new只能new具體類(lèi)啊,這咋能隱藏呢,這隱藏還有什么用呢?”。

           作者仿佛也曾經(jīng)有過(guò)我的這個(gè)傻B苦惱,它的解決方法就是:根本在使用對(duì)象的時(shí)候,特別是設(shè)計(jì)階段,盡量不去想對(duì)象是在那里被new的。他認(rèn)為:反正有了工廠模式后,你總有辦法把他們new出來(lái)的。

           所以,我用了工廠模式后更發(fā)的啟發(fā)是:以后設(shè)計(jì)的時(shí)候不要想一個(gè)Client怎么創(chuàng)建一個(gè)對(duì)象,盡管放心大膽的先繼續(xù)想,直接使用就好了。反正最后我還有工廠模式呢。

           因此俺的副標(biāo)題才是“Ignore how they were created”,呵呵。
                                        MARCO ZHANG 2006年2月16日3:52:10

    posted @ 2006-02-16 03:53 marco 閱讀(1396) | 評(píng)論 (9)編輯 收藏

    僅列出標(biāo)題  下一頁(yè)
    主站蜘蛛池模板: 亚洲第一街区偷拍街拍| 麻豆亚洲AV永久无码精品久久| 亚洲一级毛片免费在线观看| 午夜网站在线观看免费完整高清观看| 亚洲精品专区在线观看| 西西人体大胆免费视频| 亚洲av高清在线观看一区二区| 国产尤物在线视精品在亚洲| 全部免费毛片免费播放| 一本久久免费视频| 亚洲色中文字幕无码AV| 国产精品白浆在线观看免费| 久久精品国产亚洲AV天海翼| 毛片a级毛片免费播放下载| 亚洲中文字幕久久精品无码VA| 成人毛片免费在线观看| 亚洲爆乳AAA无码专区| 凹凸精品视频分类国产品免费| 深夜A级毛片视频免费| 精品亚洲一区二区三区在线播放 | 亚洲中字慕日产2021| 免费三级毛片电影片| 蜜芽亚洲av无码一区二区三区 | 亚洲天堂2017无码中文| 好爽…又高潮了免费毛片| 美女一级毛片免费观看| 亚洲精品tv久久久久久久久| 又大又硬又爽又粗又快的视频免费| 亚洲乱码卡三乱码新区| 免费国产不卡午夜福在线| 国产成人免费AV在线播放 | 成人伊人亚洲人综合网站222| 久久www免费人成精品香蕉| 亚洲一区二区电影| 日韩高清在线免费观看| 精品一区二区三区免费观看| 91亚洲国产成人精品下载| 成人免费毛片观看| 国内少妇偷人精品视频免费| 亚洲综合av一区二区三区不卡| 亚洲女人被黑人巨大进入|