<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

    2006年2月18日 #

    近期寫了個電子書的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 閱讀(3472) | 評論 (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 閱讀(2325) | 評論 (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 閱讀(2692) | 評論 (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 閱讀(627) | 評論 (0)編輯 收藏

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    posted @ 2006-02-27 07:40 marco 閱讀(868) | 評論 (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 閱讀(1285) | 評論 (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)編輯 收藏

    主站蜘蛛池模板: 国产精品亚洲а∨无码播放不卡 | 亚洲国产无套无码av电影| 日本三级在线观看免费| 久久久久亚洲AV无码专区体验| 久久福利资源网站免费看| 亚洲国产精品日韩av不卡在线| 久久久久亚洲av成人无码电影| 香蕉免费一区二区三区| 亚洲狠狠婷婷综合久久| 亚洲综合另类小说色区| 亚洲三级高清免费| 国产亚洲福利精品一区二区| 亚洲av日韩av高潮潮喷无码| 成人免费毛片观看| 成人影片一区免费观看| 亚洲私人无码综合久久网| 人人狠狠综合久久亚洲88| 亚洲人成电影网站免费| 99精品免费视品| 亚洲午夜无码久久久久小说| 国产亚洲av片在线观看播放| 毛片免费在线观看网站| 久久国产精品国产自线拍免费| 亚洲性无码一区二区三区| 午夜亚洲AV日韩AV无码大全| 四虎免费永久在线播放| 1000部拍拍拍18勿入免费视频软件| 牛牛在线精品观看免费正| 亚洲不卡中文字幕| 国产成人精品日本亚洲网站| 日韩免费视频播播| 99久热只有精品视频免费看| 免费精品视频在线| 亚洲人成人网站18禁| 亚洲高清中文字幕综合网| 亚洲精品无码永久在线观看你懂的 | 又长又大又粗又硬3p免费视频| 亚洲a级在线观看| 亚洲AV成人无码久久精品老人| 国产无遮挡吃胸膜奶免费看| **一级毛片免费完整视|