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

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

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

    我的隱式生活(My Implicit Life)

    繼續搞“對象”,玩OO.

    首頁 新隨筆 聯系 聚合 管理
      11 Posts :: 1 Stories :: 39 Comments :: 0 Trackbacks

    #

    近期寫了個電子書的C/S模式的下載工具,一個server端,一個client端。

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

    用了1,2個星期,雖然寫的很爛,但是沒有用任何第三方的產品(server or db)。

    現在里面的書籍已經接近200本了。

    注:server就用了家里的adsl,所以速度慢,關閉不定時。畢竟玩玩嘛。

    有興趣的朋友先裝個jdk1.5。再運行下面壓縮包里的exe文件執行即可。

    點此下載

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

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

    Java Collection Framwork中的類的確是最重要的基礎api,實現任何算法,基本上都很難離開它。

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

    它說這個container類庫,包含了兩大類,Collection和Map,而Collection又可以分為List和Set。當然這些抽象概念都被定義成了接口。

    話說,這樣的分類的確是嚴格按照類之間的繼承關系來說得,但是俺總覺得很別扭,真動手的時候,還是很難選擇。當然,Anytime and Anywhere使用ArrayList絕對都能解決問題,但這樣做畢竟太農民了一點。

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

    有了這兩個結構以后,JAVA就不提供Stack和Queue單獨的類了,因為,用戶可以用上面兩個類輕易的去實現。

    那Set和Map有怎么跟List連上關系呢?

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

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

    有了HashSet和HashMap。

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


    結論:

    假如你的需求是
    1:往Container中放的對象是無序且單一的;
    2:經常要檢索。
    用HashSet或HashMap吧。

    ps:這兩個條件其實是一回事,因為如果是不單一的話,你去檢索它干嘛。

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

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

    題外話:

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

    關于所有這些Container,務必記得有個輔助類叫Interator,遍歷盡量要用它。

    關于一些老的Stack、Vector、HashTable,聽說以后不要用了哦。收到啦!!

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

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

    在計算機中,文字就是字符的集合,也就是字符串,C就是因為對字符串設計的不好,才那么容易溢出。而別的一些高級語言,對于這個進行了很多的改進。

    編程的人由于技術方向和應用方向的不同,日常編程的內容差距很大。但是對于字符串的處理,那可是永遠都避不開的工作。

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

    不知道對不對,反正想出來了一個,這個基本操作就是search,這里的search的意思是:在輸入串中找到目標串的開始位置(start index),和結束位置(end index)。

    有了這個基本集,別的操作都很好衍生出來:

    局部match:其實就是要求search操作至少返回一個start index。

    全match:其實要求search操作的至少返回一個start index,并且start index要為零,end index要為輸入串的全長。

    split:其實就是search操作之后,把前一個end index和當前的start index之間的字符串截出來而已。

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

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

    這么一來,API對search的能力支持的好壞和效率高低是衡量字符串操作功能的標準,當然,如果有直接支持match,split,replace操作的話就更好了。

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

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

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

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

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

    可惜,indexOf不支持re作為參數。

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

    --------------------->>
    String類的:

    全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.

    首個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.


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

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

    可惜啊,可惜,可惜java的String類里面沒有可以支持re的search方法,那如果要用re來search,只好使用java中專門的re類庫。

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

    一般在構造中,“re的表達式”作為參數傳遞入Pattern類,“輸入串(待過濾串)”作為參數傳遞入Matcher類。

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

    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.

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

    前三個都是search方法,返回成功與否。第四個是返回當前search上的字符串。

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

    當然,Pattern和Matcher也包含直接使用re進行的match,split,replace操作。

    --------------------->>
    Patter類別的字符串操作方法

    全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.

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


    Matcher類別的字符串操作方法

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

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

    動態replace(replacement可以根據被替代的字符串變化而變化)
    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.

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

    總結:
    當必須使用re的時候,search操作就要用到Pattern,Matcher,當然動態的replace操作也要用到這兩個類。而別的match,replace,split操作,可以使用pattern,Matcher,當然也可以直接使用String,推薦還是用回咱們的String吧。

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

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

    創建和銷毀對象

    重點關注對象的創建和銷毀:什么時候、如何創建對象,什么時候、什么條件下應該避免創建對象,如何保證對象在合適的方式下被銷毀,如何在銷毀對象之前操作一些必須的清理行為。

    嘗試用靜態工廠方法代替構造器

    如果一個 client 要實例化一個對象來使用,傻 b 都知道應該先調用類的構造器來 new 一個對象,之后再調用相應的方法。除了這個方式, Java Effective 還建議了另一種方法:用靜態工廠方法來提供一個類的實例。以下的例子不反映兩者的優劣,只是反映兩者在代碼實現上的不同,優劣之后再談:

    假設咱們要一個顏色為黑色、長度為 50cm 的錘子,自然就用構造器創建一個

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

    而用靜態工廠方法來實例化一個對象,如下

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

    也可以用專門的一個工廠類來實例化

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

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

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

    下面就說說用靜態工廠代替構造器的好處( advantage )和不好處( disadvantage )。

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

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

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

    Hammer Color c, int kg {

    //remainder omited

    }

    2 :第一個參數是顏色( Color 型),第二個參數是錘子的長度( int 型)。

    Hammer Color c, int cm {

    //remainder omited

    }

    感覺滿足需要了,但是細心一看,完了,構造器的參數列表類型重復了,肯定編譯通不過,這是面向對象構造器天生的缺陷——唯一的變化就是參數,參數都分辨不了,就真的分辨不了。

    而另外就算參數能分辨的了,構造器一多,它的參數一多,您根本就不知道每個參數是用來干什么的,只能去查閱文檔,在您已經眼花繚亂的時候再去查文檔,一個一個的對,折磨人的活。

    這個時候,您就可以考慮用靜態工廠方法來實例化對象了。因為靜態工廠方法有一個最簡單的特點就是:他有可以變化的方法名(構造器的名字變不了)。用名字的不同來代表不同的構造需要,這么簡單的普通的特點在這里就是它相對于構造器的 advantage

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

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

    //remainder omited

    }

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

    //remainder omited

    }

    這是不是一目了然多了。嗯,我是這樣認為的。

    第二個好處,“靜態工廠方法不需要每次都真的去實例化一個對象”——其實這也是另一些優化方法的前提。

    構造器的每次 invoke 必定會產生一個新的對象,而靜態工廠方法經過一定的控制,完全可以不用每次 invoke 都生成一個新的對象。

    為什么不每次都生成一個對象的原因就不必說了,因為原因太明顯。這個原因就是為什么要“共享”對象的原因。

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

    1 :單例模式的需要,一旦需要某個對象有單例的需要,必定對于這類對象的構造只能用靜態工廠方法了。

    2 flyweight 模式和不變( immutable 模式的需要,這兩個模式很多時候都說一起使用的,一旦一些對象我們認為是不變的,那自然就想拿來重用,也就說共享,而 flyweight 就是用來重用這些小粒度對象的。

    Boolean.valueOf (boolean) 方法:

    Boolean a = Boolean.valueOf (100);

    Boolean b = Boolean.valueOf (100);

    ?a,??b兩個引用都是指向同一個對象。

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

    這種一個狀態(如上面一個數字)對應的對象只有一個還有一個好處,就是可以直接通過比較“引用”來判斷他們是否 equel (這里的 equel 是邏輯相等的意思),以前需要 a.equels(b) ,而一旦用“ flyweight 模式和不變( immutable 模式”后,避免了產生多余的相同對象,用 a==b 就可以達到 a.equels(b) 的目的了。這樣當然優化了 performance ??

    第三個好處,其實就是工廠方法的核心好處——我把它稱為“抽象類型構造器”。它可以為我們提供一個抽象類型的實例,同時必要的隱藏了抽象類型的具體結構。這是 new 怎么都達不到的。

    這種模式的好處其實就是面向對象的最核心的好處,抽象和具體可以分離,一旦抽象定義好了,具體的東西可以慢慢的變化,慢慢的拓展——開閉原則。

    Collections Framework API ,都是描述集合類型的接口,也就是對于客戶端來看,只有 Collection 這個類要認識,而實際上,實現這個接口的 Collection 是多種多樣的。如果要讓用戶都知道這些具體實現的 Collection ,就增加了復雜度。

    這時,通過一個靜態工廠方法,就可以隱藏各種 Collection 的具體實現,而讓 Client 只使用返回的 Collection 對象就可以了。

    這里還可以加上一些權限控制,如這些實現只要對于工廠來講是可以訪問的,不用是 public 的,而他們只要通過 public 的工廠就可以提供給用戶。非常有利于代碼的安全。

    靜態工廠方法的第一個缺點就是:使用靜態工廠方法創建的類的構造器經常都是非公共或非 protected 的。 這樣,以后這些類就沒有辦法被繼承了。不過也有人說,不用繼承就用 composition 唄。也是!呵呵。

    靜態工廠方法的第二個缺點是:在 jdk 文檔里,這些靜態工廠方法很難跟別的靜態方法相區別。 而文檔中,構造器是很容易看到的。

    為了一定程度解決這個問題,我們可以用一些比較特別的名字來給這類靜態工廠方法來命名。最常用的有:

    valueOf —— 用來放回跟參數“相同值”的對象。

    getInstance —— 返回一個對象的實例。單例模式中,就是返回單例對象。

    總結:靜態工廠方法和構造器都有各自的特點。最好在考慮用構造器之前能先考慮一下靜態工廠方法,往往,后者更有用一點。如果權衡了以后也看不出那個好用一些,那就用構造器,畢竟簡單本分多了。

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

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

    昨天,以前師兄做的系統因為漏洞又被投訴,頭讓俺做個presentation給實驗室下一級同學總結一下,避免以后再犯錯。今天講完了就放上來,存個證,以后也好翻閱溝通。

    不涉及主機安全、網絡安全、數據庫安全,只從Web應用程序的角度,整理歸納一下面臨的主要安全問題。

    點擊看大圖。
    r_Web應用程序安全問題.jpg

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

    這幾天瞄了幾本設計模式的書,沒有細看具體模式啦,而是老是琢磨那些深奧無比的話。這些話經常出現在計算機的書籍中,很有禪意,也有哲理。聽說,高手就喜歡寫點這樣的話。

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

    其中,最高指導的一個就是“開-閉”原則。別的原則,里氏代換原則、依賴倒置原則、組合/聚合復用原則和迪米特法則都是為了達到“開-閉”原則而出現的規則。

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

    分析對象的時候,要多分析設計“抽象”的概念,對象之間的聯系要多基于抽象的概念而不是具體,這樣具體才能能夠變化,這樣才是開閉。用我自己的話就是要“游走于 抽象”。

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

    還有一個必須記住的是:運用抽象以后,客戶端的使用發生了巨大的變化。不再是指那兒用那兒。而是要做更多的準備工作,因為運用抽象,本身就把具體“組合”的職責推遲到使用的階段。那誰使用,肯定是客戶端。所以,客戶端的使用要革新。要習慣用工廠,習慣把一系列的抽象定具體了,并按照一定方式“組合”起來用。而且,最終要善于用接口來調用方法。

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

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

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


    “共享”的思想

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

     

    之于OO的共享

     

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

     

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

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

    2.         為了控制“對象”的存儲空間,我們需要共享。畢竟,目前來說,系統的memory還是編程時最珍貴的資源。

    3.         為了優化“對象”的創建消耗,我們需要共享。如果,一個對象的創建過程消耗太大,系統不能支持頻繁的創建,共享的使用它也是一個好主意。

    4.         等等。

     

    而在實際的應用中,往往我并沒有細想“我為什么使用共享?”,已經不自覺的就用了;如果真的認真分析起來,基于的環境也是多樣,并不會只是上面的其中一種。

     

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

    1.         Singleton模式”:一個class就一個對象實例,大家都用它,滿足context1

    2.         pool技術”:只提供一定數目的對象,大家都用他們,實現context2context3

    3.         flyweight模式”:一個class的一個狀態就一個對象實例,實現一個狀態對象的共享,實現context2context3

     

    使用時要注意的地方:

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

    2.         確認thread safe。當共享的對象可能被多個線程共享時,這是不可以回避的問題。

    3.         應對對象狀態的變化。一旦共享的對象發生了變化,我們怎么處理?改變之,舍棄之?也是我們需要確定的。

     

    項目中的應用:

     

    項目需求:

    為學校的同學提供Web查詢,查詢的內容有很多。其中,“查課表”、“查考表”是最為關鍵的需求,以后可能還要提供“查詢空閑自習教室”的功能。

    在這些查詢中,有一個共同點,就是都涉及“教室”這一對象。“查課表”時要告訴同學在哪個教室上課,“查考表”時要告訴同學在哪個教室考試,等等。

     

    數據庫設計:

    對于“查課表”用例,有關的數據庫設計如下:
    o_5-1-1.jpg 


    對象層的設計:

    o_5-1.JPG
     

    學生每查詢一門課程的課表,系統就會sql查詢“視圖V_LESSONSCHEDULE”,進而生成一個LessonSchedule對象,然后返回給用戶顯示。當然,在生成這個LessonSchedule對象的過程中,屬于它的Classroom對象,以及更深一步的BuildingArea對象都會生成。下面就是這個過程的順序圖:

     

    o_5-2.JPG 


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

    1.         查數據庫中的教室、教學樓、校區的信息;

    2.         創建相應的“教室對象”(包括了屬于它的“教學樓”對象和“校區”對象)。

     

    考慮共享“教室”對象

    “教室”對象一旦可以生成以后,完全可以給后續共享使用,不必要每個查詢都要去生成。

     

    詳細說是基于下面的考慮:

    1.         這類查詢用例(查課表,查考表)發生的頻繁很高很高,也就是說,一旦讓用戶查起來,系統中會產生大量的“教室”對象這類對象,應該說會占很大的內存空間。

    2.         共享“教室”對象后,可以減少對數據庫的查詢次數,并降低了查詢粒度(以前是基于二級視圖查詢,現在可以基于基本表查詢),提高了一點數據庫查詢性能。

     

    當然,同時我腦袋中也有反對的聲音:

    1.         雖說,這類查詢會產生很多相同的“教室”對象,但是JVM本生提供的垃圾回收功能完全可以處理它。除非,“同時”有很多很多這類對象都在被使用,根本回收不了,才會造成內存短缺。

    2.         如果,我以某種共享機制讓這些“教室”對象,在系統中存在下來(延長了生命周期)了。而它們本身的數目就很多(如,我們學校就有××),可能還沒有等你用上,系統已經掛了。另外,如果不是同時有很多查詢需要,我留這么多“教室”對象在系統里,反而是占了內存,而不是優化了內存的使用。

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

     

    結論:

    經過我們分析,系統對于“教室”對象的重復使用,頻繁程度非常高。一般,有10個人同時使用,就有5個人左右涉及的用例要使用“教室”對象。把它共享起來,還是有一定必要的。

     

    進一步考慮:

    而實際上,我們可以進一步共享下去:

    除了讓客戶端共享“教室對象(Classroom)”外,還可以讓“教室對象”共享“教學樓對象(Building)”,和讓“教學樓對象”共享“校區對象(Area)”。

    因此,最終的共享是在三級上都實現。

     

    Flyweight模式:

     

    特點:

    書上說:“享元模式可以使系統中的大量小粒度對象被共享使用”。第一,對象出現的量要大,想必這比較好理解,很少使用的也就沒有必要共享了;第二,要小粒度,我比較納悶?難道對于大粒度對象就不行嗎?可能書上認為,大粒度對象的共享已經占了比較大的空間,沒有小對象那么有效吧。

     

    另外,書上還說,要使用“享元模式”,被共享的對象的狀態(類別)要比較固定,這樣就可以為每一個狀態僅僅創建一個對象。當然,如果每次使用對象時,對象的狀態都是不一樣的,那就根本不存在共享這些對象的必要了。

     

    聯系項目思考:

    基于上面對項目的分析,“教室”、“教學樓”、“校區”對象都是在系統中會被大量使用的對象,而且粒度的確比較小;并且它們有固定的類別,而且不易改變。如校區對象,暫時就有4個。教學樓可能4050個左右。很適合“享元模式”的使用環境。

     

    確定共享方式:

    1.         確定共享對象的scope。在本web程序中,這些共享對象的scope理應是application,而更簡單的一個作法就是把這些對象設為static,我選擇后者。

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

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

     

    設計類圖:

    o_5-3.JPG 

    當然,也可以把這些工廠都設計成“Singleton模式”的,使它們只會有一個實例。

     

    客戶端使用:

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

     

    實例代碼

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

    oneClassroomtwoClassroomoneBuildingtwoBuildingoneAreatwoArea由于都是32號課程的東西,根據我們的設計意圖,應該實現共享。

    而實際上,它們每對的確是同一個對象的引用。因此,實現了預期的設想。

     

    Review

    在本項目中,當第一次設計出來的時候,我們發現了某些對象恰好有共享的需要。

     

    而更多的實際情況是,這些需要共享的“信息或狀態”在設計中并不是那么恰好的表現為“一個對象”的粒度,而是要不就包含在一個對象內部,要不就跨幾個對象。在這樣的情況下,共享的設計更多是發生在代碼重構階段而不是第一的設計階段。當然,為了共享對象而做出的代碼重構,最重要的一步就是把需要共享的“信息或狀態”設計成為新的對象。

     

    對于,“享元模式”來說,就是要把需要共享的“信息或狀態”設計成“享元對象”。別的在此就不說了,因為我也不懂了,呵呵。


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

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

    廢話:

    預料中的日志3暫時生產不出來,話說難產就好,別夭折就行,有點掉價哦,呵呵。

     

    因為有些東西還沒有想清楚。那就先搞個四吧,這個東西還是清楚那么一點的。

    一版描述:

    項目需求

    使每個servlet能對用戶訪問權限進行檢查。簡單來說,就是要給每個servlet加個鎖,有鑰匙的用戶才能訪問。

     

    而項目中用戶所謂的訪問權限是基于他擁有的角色。也就是說,servlet對用戶訪問權限的檢查,就是對他所擁有角色的檢查。暫時,每個用戶只能擁有一個角色。

     

    項目的角色很多,但是在web端暫時只有如下的三種:

     

    項目暫時角色

    游客,學生,教師

     

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

     

    servlet加鎖方式

     游客,學生,教師,
    游客或學生,游客或教師,學生或教師,
    游客或學生或教師

     注:上面的“游客”就是“游客角色有權訪問”的意思,依此類推。

     

    這里只有關系“或”(||),如果一個servlet的加鎖方式是“學生或教師”,也就是說擁有學生或教師角色的用戶都可以訪問它。關系“與”(&&)在這里不太可能存在,因為沒有需求說:某個servlet一定只能由“既是學生又是教師的用戶”才能訪問,而且前面也說了,暫時一個用戶“有且只有”一個角色。

     

    So we can get following function


    “加鎖方式數” = 2的“角色數”次方 - 1


    “加鎖方式數”是關于“角色數”的指數函數,也就是說它是關于“角色數”成“指數級”增長的,應該說很快了吧。


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

     

    運用OO的最基本方式,就是封裝對象。既然有為servlet“看門”的責任,那就把這個責任封裝成一個對象,用個俗名:validator

     

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

    o_4-1.JPG

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

     

    這樣,對于一個servlet,想讓它上什么樣的鎖,只要讓它拿到對應的子類的引用即可,如下圖中的ClientServlet,我們規定只能有“學生或教師”才能訪問它。它的部分代碼便是:


    o_4-2.JPG

     

     

    1//new對應的Validator接口的子類。
    2//這里是學生或教師可訪問,因此要new Student_Or_Teacher_Validator
    3Validator validator = new Student_Validator();
    4//然后調用驗證方法就可以了
    5boolean ok = validator.validate();

    至此,第一個解決方案就出來了。

    思考:

    不足

    validator接口的子類數目隨“角色數”成“指數級”增長,數量太多;而且子類中重復邏輯的代碼很多,如“Student_Or_Teacher_Validator”就重復了“Student_Validator”和“Teacher_Validator”的邏輯,萬一“Student_Validator”的邏輯要改,只要涉及Student的子類都要跟著改,維護上不方便。

     

    進一步改進的可能

    Student_Or_Teacher_Validator類”的validate方法很大程度上就是“Student_Validator類”的validate方法和“Teacher_Validator類”的validate方法“或操作”出來的結果。

     

    因此,能不能考慮由“Student_Validator類的validate方法”和“Teacher_Validatorvalidate方法”動態的構造一個功能如“Student_Or_Teacher_Validator類的validate方法”。

     

    這樣,“Student_Or_Teacher_Validator”就可以省略了,只剩下一些原子的“角色類”。要實現Student_Or_Teacher_Validator的驗證功能,拿Student_ValidatorTeacher_Validator裝配一下就可以了。

     

    結論,需要根據實際情況,動態的改變(裝配)“Validator接口對象”的validate方法。

     

    第一個火花就是“裝飾模式”。它可以讓客戶端動態的組裝對象的方法。真神奇!

    第二版來啦

    o_4.JPG

     

    注:上圖中出現了AndRelationAnd的一系列角色類,可以暫時省略不看,因為前面說了,現在還沒有“與”關系這個需求。只看Or的就可以。

     

    我喜歡叫這個模式為洋蔥模式,一層包一層,最外層對象某方法的邏輯是由內部一層一層對象的同一方法組合出來的。

     

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

     

    如要生成一版中的Student_Or_Teacher_Validator對象,可以用Or_StudentOr_Teacher兩個對象“Or”出來:

     

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

     

    如要生成一版中的Guest_Or_Student_Or_Teacher_Validator對象,可以用Or_StudentOr_TeacherOr _Guest三個對象“Or”出來:

     

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

     

     

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

    對客戶端的影響:

    一版中,客戶端要什么樣的驗證類,就直接使用具體類。

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

    優點:

    相對一版最明顯的優點就是類的數目少了很多。

     

    一版不是說“指數級”嗎?這里只是線性的了。假設某一天系統拓展到有10個角色,一版就有210次方那么多個,也就是1024個驗證類。

     

    而二版還是10個角色類,別的都可以在客戶端使用的時候,動態的組裝

     

    更重要的是代碼結構好了,重復邏輯少了。每個邏輯都以最atomic的大小放到最應該的地方。

     

    進而,維護的代價少多了。如某天“教師角色”的驗證邏輯發生了變化,只要改動Or_Teacher一個地方即可。

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

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

        說起這個工廠模式,一時還真不知道該如何說起。反正這是我的開發日志,不提理論的東西,理論的東西那里都有,我只想把具體實踐記錄下來給師弟師妹們作個參考,積累點經驗。所有這些文字都是集中講一點――“在什么情況下為什么用某種模式好,為什么好,為什么在那種情況下能想起來用?”。

           研究生院項目中“明顯”使用了“工廠方法模式”。其實在遇到具體問題的時候,即使我們不知道有這個模式存在,我們也肯定會造一個類似的東西出來。但是,肯定沒有書上論述的那么好,那么全面。我想這就是看書的好處吧。

     

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

     

           剛開始使用這個東西的時候,只是感覺是單純的一種模式,用于創建需要的對象。

           但是隨著使用和思考的深入,越發發現它給我的啟示不只在于單純的對象創建,而是告訴我應該怎么理解“產品”,怎么得到“產品”,怎么消費“產品”,以至于以后怎么設計“產品”。

           下面這個線索是我對它出現必然性的理解:

    1.         “針對接口編程”

      這是OO世界中經典的規范,不管你主動還是被動,你天天都在用這個東西。

        接口是共性的表示,在對象的世界中,共性和個性的辯證關系是最重要的關系。在萬千的對象中,通過它們之間的共性和個性,可以形成最基本對象層級架構。

    假設我們的討論域中有以下一些對象:“學生”、“大學生”、“小學生”、“中學生”;我們不用細想,學過一天OO的人都可以為這些耳熟能詳的對象們,通過個性和共性的關系得出下面的結構圖。

    o_1.JPG

     

           把這些對象之間的關系定義成這樣是順理成章的。

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

     

    o_2.JPG

    2.         接口和具體類的矛盾

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

    但這里出現的困難是,接口都是“假”的,都是由具體類upcast的。

    如果Client要使用接口StudentClient中必須會出現下面的代碼:

    Student marco = new Small_Student();

           只要一出現這個代碼,就說明Client不只跟Student打交道了,它知道了Small_Student類,這違反了我們預先的想法。

    3.         找“人”幫我去創建“接口對象”

        從上圖體現出來的結構看,Client只想跟Student打交道的目的是實現不了的了。

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

    o_3.JPG

        這樣,Client就可以既用到了“Student接口對象”,又不用因為“只有具體類才能創建對象”的規則,而必須對其子類結構有完全的了解。它成功的解決了2中的矛盾。

           而“負責創建具體類對象的任務”全部都落在了這個“幫手”身上,這個“幫手”(Student_Factory)就是工廠模式中的工廠類,更具體的說,它就是“簡單工廠模式”中的“簡單工廠類”。

           我覺得,即使一點都不知道工廠模式,一旦我遇到了2里說的矛盾,我也會用這樣的方法處理。

    4.         這個“幫手”不符合“開-閉原則”

    這個幫手的確不錯了,而且一躍成為系統中最重要的對象了,所有“創建具體類的邏輯”都放進去了,也就是因為重要,萬一掛了不就慘了。

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

    o_4.JPG

    5.         讓“幫手”也多態

    這里可以嘗試讓“幫手”也多態一下,這樣“每種學生類創建的任務”都被分派到了多態出來的類中去了。這時,再有新的學生類型加進來,添加一個對應的幫手就可以了。這樣雖然類多了一些,但是符合“開-閉”原則,書上稱之為“工廠方法模式”。如圖:

    o_5.JPG

        假如Client現在要使用一個小學生,代碼如下:

    1        //創建一個小學生工廠類這個幫手
    2        Student_Factory factory = new Small_Student_Factory();
    3        //求助這個幫手,幫我創建一個

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

    6        primaryStudent.playGames();
    7

        在這里還是要強調兩點:

    n         雖然實際上Client的確使用了一個小學生對象(Small_Student),但這里Client也認為它就是Student對象,這里一定要用Student接口來隱藏它的具體類。

    n         另外,卻不需要用Student_Factory這個接口來隱藏它的具體類,因為,Client實際就是通過“選擇它的具體類”這招兒來“選擇創建的學生類型”。這里的Student_Factory更多的功能不是“隱藏”具體類,而是“規范”具體類。

     

    項目實踐

     

           扯淡到此,該聯系我們的項目啦。

           由于是做研究生院的項目,其中巨大的需求就是要讓同學能在網上提交各種申請單,申請退學的,申請轉專業的,申請復學的,申請保留學籍的,除了申請女朋友的外,應有盡有。       對于這些單子,用最最基本OO思維,根據共性個性分析方式,抽象出一個申請單接口,和若干的具體類。

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

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

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

     

    o_6.JPG

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

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

    o_7.JPG

           Bean_Shift就是工廠接口,相當于前面的Student_Factory接口。還有很多的具體類就是生產各種申請單的工廠類。

           下面就是使用“申請單工廠方法模式”的一段客戶端代碼:

     1        //聲明申請單接口
     2        Ent_Shift es = null;
     3        //聲明申請單工廠接口

     4        Bean_Shift bs = null;
     5        //根據傳入的申請單類型數字身成對應的申請單工廠

     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             //省略了別的申請單……………………

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

    20
    21        try 
    {
    22            //調用工廠接口的生產方法

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

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

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

     

    升華

     

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

           我每當看到這時,老是會被一種思路困擾――“new只能new具體類啊,這咋能隱藏呢,這隱藏還有什么用呢?”。

           作者仿佛也曾經有過我的這個傻B苦惱,它的解決方法就是:根本在使用對象的時候,特別是設計階段,盡量不去想對象是在那里被new的。他認為:反正有了工廠模式后,你總有辦法把他們new出來的。

           所以,我用了工廠模式后更發的啟發是:以后設計的時候不要想一個Client怎么創建一個對象,盡管放心大膽的先繼續想,直接使用就好了。反正最后我還有工廠模式呢。

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

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

    僅列出標題
    共2頁: 1 2 下一頁 
    主站蜘蛛池模板: 亚洲三级在线免费观看| 精品韩国亚洲av无码不卡区 | 久草视频免费在线| 狼人大香伊蕉国产WWW亚洲| 亚洲人成无码网站| 一本无码人妻在中文字幕免费| 美女啪啪网站又黄又免费| 久久亚洲AV午夜福利精品一区 | 亚洲va无码手机在线电影| 99久久免费精品国产72精品九九| 日本永久免费a∨在线视频| 亚洲综合激情九月婷婷| 国产hs免费高清在线观看| 日本免费一区二区三区四区五六区 | 国产免费观看a大片的网站| 日韩免费在线视频| 亚洲av无码无线在线观看| 亚洲色图国产精品| 亚洲AⅤ永久无码精品AA| 8090在线观看免费观看| 特级av毛片免费观看| 亚洲黄色在线视频| 亚洲爽爽一区二区三区| 性做久久久久久久免费看| 99久久免费国产精品热| 亚洲av成人无码网站…| 亚洲视频一区在线| 亚洲欭美日韩颜射在线二| 暖暖在线日本免费中文| 1000部无遮挡拍拍拍免费视频观看| 理论片在线观看免费| 亚洲综合综合在线| 久久久综合亚洲色一区二区三区 | 亚洲日韩精品A∨片无码加勒比| 亚洲国产精品无码AAA片| 亚洲高清视频一视频二视频三| 麻豆最新国产剧情AV原创免费| 精品成人免费自拍视频| 国产精品免费视频观看拍拍| 亚洲av无码专区在线观看亚 | 成人性生交大片免费看好|