隨筆-14  評(píng)論-25  文章-1  trackbacks-0
            2014年6月21日
          在一個(gè)項(xiàng)目里面有這么一個(gè)技術(shù)需求:
          1.集合中元素個(gè)數(shù),10M
          2.根據(jù)上限和下限從一個(gè)Set中過(guò)濾出滿足要求的元素集合.

          實(shí)際這個(gè)是個(gè)很典型的技術(shù)要求, 之前的項(xiàng)目也遇見過(guò),但是因?yàn)楫?dāng)時(shí)的類庫(kù)不多, 都是直接手寫實(shí)現(xiàn)的. 方式基本等同于第一個(gè)方式.

          在這個(gè)過(guò)程中, 我寫了四個(gè)方式, 基本記錄到下面.
          第一個(gè)方式:對(duì)Set進(jìn)行迭代器遍歷, 判斷每個(gè)元素是否都在上限和下限范圍中.如果滿足則添加到結(jié)果集合中, 最后返回結(jié)果集合.
                      測(cè)試效果:集合大小100K, 運(yùn)算時(shí)間 3000ms+
          過(guò)濾部分的邏輯如下:
           1     void filerSet(Set<BigDecimal> targetSet, String lower, String higher) {
           2         BigDecimal bdLower = new BigDecimal(Double.parseDouble(lower));
           3         BigDecimal bdHigher = new BigDecimal(Double.parseDouble(higher));
           4 
           5         Set<BigDecimal> returnSet = new HashSet<BigDecimal>();
           6         for (BigDecimal object : targetSet) {
           7             if (isInRange(object, bdLower, bdHigher)) {
           8                 returnSet.add(object);
           9             }
          10         }
          11     }
          12 
          13     private boolean isInRange(BigDecimal object, BigDecimal bdLower,
          14             BigDecimal bdHigher) {
          15         return object.compareTo(bdLower) >= 0
          16                 && object.compareTo(bdHigher) <= 0;
          17     }
          第二個(gè)方式: 借助TreeSet, 原始集合進(jìn)行排序, 然后直接subset.
                      測(cè)試效果: 集合大小10M, 運(yùn)算時(shí)間: 12000ms+(獲得TreeSet) , 200ms(獲得結(jié)果)
          過(guò)濾部分的邏輯如下(非常繁瑣):
            1     Set<BigDecimal> getSubSet(TreeSet<BigDecimal> targetSet, String lower,
            2             String higher) {
            3 
            4         BigDecimal bdLower = new BigDecimal(Double.parseDouble(lower));
            5         BigDecimal bdHigher = new BigDecimal(Double.parseDouble(higher));
            6 
            7         if ((bdHigher.compareTo(targetSet.first()) == -1)
            8                 || (bdLower.compareTo(targetSet.last()) == 1)) {
            9             return null;
           10         }
           11 
           12         boolean hasLower = targetSet.contains(bdLower);
           13         boolean hasHigher = targetSet.contains(bdHigher);
           14         if (hasLower) {
           15             if (hasHigher) {
           16                 System.out.println("get start:" + bdLower);
           17                 System.out.println("get end:" + bdHigher);
           18                 return targetSet.subSet(bdLower, true, bdHigher, true);
           19             } else {
           20                 BigDecimal newEnd = null;
           21                 System.out.println("get start:" + bdLower);
           22                 SortedSet<BigDecimal> returnSet = null;
           23                 if (bdHigher.compareTo(targetSet.last()) != -1) {
           24                     newEnd = targetSet.last();
           25                 } else {
           26                     SortedSet<BigDecimal> newTargetSet = targetSet
           27                             .tailSet(bdLower);
           28                     for (BigDecimal object : newTargetSet) {
           29                         if (object.compareTo(bdHigher) == 1) {
           30                             newEnd = object;
           31                             break;
           32                         } else if (object.compareTo(bdHigher) == 0) {
           33                             newEnd = object;
           34                             break;
           35                         }
           36                     }
           37                 }
           38                 returnSet = targetSet.subSet(bdLower, true, newEnd, true);
           39                 if (newEnd.compareTo(bdHigher) == 1) {
           40                     returnSet.remove(newEnd);
           41                 }
           42                 return returnSet;
           43             }
           44 
           45         } else {
           46             if (hasHigher) {
           47                 System.out.println("get end:" + bdHigher);
           48                 TreeSet<BigDecimal> newTargetSet = (TreeSet<BigDecimal>) targetSet
           49                         .headSet(bdHigher, true);
           50                 BigDecimal newStart = null;
           51                 SortedSet<BigDecimal> returnSet = null;
           52 
           53                 if (bdLower.compareTo(targetSet.first()) == -1) {
           54                     newStart = targetSet.first();
           55                 } else {
           56                     for (BigDecimal object : newTargetSet) {
           57                         if (object.compareTo(bdLower) != -1) {
           58                             newStart = object;
           59                             break;
           60                         }
           61                     }
           62                 }
           63                 returnSet = targetSet.subSet(newStart, true, bdHigher, true);
           64 
           65                 return returnSet;
           66             } else {
           67                 System.out.println("Not get start:" + bdLower);
           68                 System.out.println("Not get end:" + bdHigher);
           69                 BigDecimal newStart = null;
           70                 BigDecimal newEnd = null;
           71                 if (bdHigher.compareTo(targetSet.last()) != -1) {
           72                     newEnd = targetSet.last();
           73                 }
           74                 if (bdLower.compareTo(targetSet.first()) == -1) {
           75                     newStart = targetSet.first();
           76                 }
           77                 for (BigDecimal object : targetSet) {
           78                     if (newStart == null) {
           79                         if (object.compareTo(bdLower) != -1) {
           80                             newStart = object;
           81                             if (newEnd != null) {
           82                                 break;
           83                             }
           84                         }
           85                     }
           86 
           87                     if (newEnd == null) {
           88                         if (object.compareTo(bdHigher) != -1) {
           89                             newEnd = object;
           90                             if (newStart != null) {
           91                                 break;
           92                             }
           93                         }
           94                     }
           95                 }
           96 
           97                 if (newStart == null) {
           98                     if (newEnd == null) {
           99                         if ((bdHigher.compareTo(targetSet.first()) == -1)
          100                                 || (bdLower.compareTo(targetSet.last()) == 1)) {
          101                             return null;
          102                         }
          103                         return targetSet;
          104                     } else {
          105                         SortedSet<BigDecimal> newTargetSet = targetSet.headSet(
          106                                 newEnd, true);
          107                         if (newEnd.compareTo(bdHigher) == 1) {
          108                             newTargetSet.remove(newEnd);
          109                         }
          110                         return newTargetSet;
          111                     }
          112                 } else {
          113                     if (newEnd == null) {
          114                         SortedSet<BigDecimal> newTargetSet = targetSet.tailSet(
          115                                 newStart, true);
          116                         return newTargetSet;
          117                     } else {
          118                         SortedSet<BigDecimal> newTargetSet = targetSet.subSet(
          119                                 newStart, true, newEnd, true);
          120                         if (newEnd.compareTo(bdHigher) == 1) {
          121                             newTargetSet.remove(newEnd);
          122                         }
          123                         return newTargetSet;
          124                     }
          125                 }
          126             }
          127         }
          128     }
          第三種方式: 使用Apache Commons Collections, 直接對(duì)于原始Set進(jìn)行filter.
                      測(cè)試效果:集合大小10M,過(guò)濾結(jié)果1M, 運(yùn)算時(shí)間: 1000ms+
          過(guò)濾部分的代碼如下:
           1 //過(guò)濾的主體邏輯
           2     void filterSet(Set<BigDecimal> targetSet, String lower, String higher) {
           3         final BigDecimal bdLower = new BigDecimal(Double.parseDouble(lower));
           4         final BigDecimal bdHigher = new BigDecimal(Double.parseDouble(higher));
           5 
           6         Predicate predicate = new Predicate() {
           7             public boolean evaluate(Object object) {
           8                 BigDecimal bDObject = (BigDecimal) object;
           9                 return bDObject.compareTo(bdLower) >= 0
          10                         && bDObject.compareTo(bdHigher) <= 0;
          11             }
          12         };
          13 
          14         CollectionUtils.filter(targetSet, predicate);
          15     }

          第四種方式:使用Guava(google Collections), 直接對(duì)于原始Set進(jìn)行Filter
                      測(cè)試效果:集合大小10M,過(guò)濾結(jié)果1M, 運(yùn)算時(shí)間: 100ms-
          過(guò)濾部分的代碼如下:
           1 //guava filter
           2 
           3     Set<BigDecimal> filterSet(Set<BigDecimal> targetSet, String lower,
           4             String higher) {
           5         final BigDecimal bdLower = new BigDecimal(Double.parseDouble(lower));
           6         final BigDecimal bdHigher = new BigDecimal(Double.parseDouble(higher));
           7 
           8         Set<BigDecimal> filterCollection = Sets.filter(targetSet,
           9                 new Predicate<BigDecimal>() {
          10                     @Override
          11                     public boolean apply(BigDecimal input) {
          12                         BigDecimal bDObject = (BigDecimal) input;
          13                         return bDObject.compareTo(bdLower) >= 0
          14                                 && bDObject.compareTo(bdHigher) <= 0;
          15                     }
          16                 });
          17 
          18         return filterCollection;
          19     }


          四種方式對(duì)比如下:
          第一種方式:  僅依賴于JAVA原生類庫(kù) 遍歷時(shí)間最慢, 代碼量很小
          第二種方式:  僅依賴于JAVA原生類庫(kù) 遍歷時(shí)間比較慢(主要慢在生成有序Set), 代碼量最多
          第三種方式:  依賴于Apache Commons Collections, 遍歷時(shí)間比較快, 代碼量很少
          第四種方式:  依賴于Guava, 遍歷時(shí)間最快, 代碼量很少

          基于目前個(gè)人的技術(shù)水平和視野, 第四種方式可能是最佳選擇.

          記錄一下, 以后可能還會(huì)有更好的方案.




          posted @ 2014-06-21 23:33 混沌中立 閱讀(7387) | 評(píng)論 (10)編輯 收藏
            2009年9月2日
          在幾年之前,在大學(xué)里面的時(shí)候,認(rèn)為系統(tǒng)的架構(gòu)設(shè)計(jì),就像建筑設(shè)計(jì)一樣,會(huì)把骨架搭成,然后有具體人員進(jìn)行詳細(xì)的開發(fā).

          在后來(lái),工作中,慢慢有了一些變化,因?yàn)樵鹊南敕ú惶泻蠈?shí)際,系統(tǒng)就是在變化之中的,如果固定了骨架,那就很難的敏捷面對(duì)變化.
          所以,系統(tǒng)的架構(gòu)設(shè)計(jì),應(yīng)該是面向接口的設(shè)計(jì),確定各個(gè)部分之間的數(shù)據(jù)接口和方法接口.這樣,即使有了變化,只要遵循接口的定義,就還是可以面對(duì)變化.


          最近,又有了想法的變化.架構(gòu)的設(shè)計(jì),應(yīng)該就是規(guī)則和規(guī)約的設(shè)計(jì).設(shè)計(jì)出一系列,統(tǒng)一的,和諧的規(guī)則,在這些規(guī)則之前圈住的部分,實(shí)際就是系統(tǒng)的全貌.
          接口的設(shè)計(jì),實(shí)際只是規(guī)則和規(guī)約設(shè)計(jì)的一個(gè)部分.
          架構(gòu)的設(shè)計(jì),不應(yīng)該只是程序方面的事情,同時(shí)也包含了心理學(xué)方面和社會(huì)學(xué)方面的一些規(guī)則.例如:團(tuán)隊(duì)在面對(duì)變化時(shí)候,需要采用的規(guī)則和流程.
          只有包含了這些非程序上的規(guī)則之后,才能保證架構(gòu)風(fēng)格的統(tǒng)一協(xié)調(diào).


          以上,是我對(duì)系統(tǒng)設(shè)計(jì)的想法的轉(zhuǎn)變過(guò)程.記錄于此,以供回溯.




          posted @ 2009-09-02 10:53 混沌中立 閱讀(253) | 評(píng)論 (0)編輯 收藏
            2009年8月20日
          面對(duì)著滿屏幕的程序
          是三年前,項(xiàng)目剛剛啟動(dòng)的時(shí)候,同事寫的代碼.
          三年過(guò)去了,項(xiàng)目由第一期變成了第七期.

          這段代碼還是在這里,有個(gè)屬性是list,其中每個(gè)cell都是一個(gè)長(zhǎng)度18的String數(shù)組.
          數(shù)組里面放置了所需要導(dǎo)出到頁(yè)面table的內(nèi)容.

          現(xiàn)在要開始修改了,需要向頁(yè)面的table中增加4列.
          繁瑣的讓人要命的工作,需要跟蹤這個(gè)循環(huán),判斷每個(gè)pattern下面,這個(gè)長(zhǎng)度18的數(shù)組里面放了哪些內(nèi)容.

          好吧,對(duì)象化維護(hù)從數(shù)組開始,把數(shù)組對(duì)折,因?yàn)檫@個(gè)數(shù)組時(shí)一個(gè)比較數(shù)組,前面9個(gè)元素是之前的情況,后面9個(gè)事之后的情況.
          用一個(gè)bean,放入兩次就可以了.但是bean中,需要一個(gè)標(biāo)志,標(biāo)識(shí)是之前的情況還是之后的情況.

          同時(shí)需要一個(gè)transform方法,把之前從幾個(gè)來(lái)源過(guò)來(lái)的情況,變成bean的屬性.
          接下來(lái)需要一個(gè)values方法,把bean里面的屬性直接按順序轉(zhuǎn)化成數(shù)組.
          本期新增的4個(gè)屬性,直接放入bean中就可以了.

          這樣原來(lái)很復(fù)雜的數(shù)組,就可以簡(jiǎn)單的用對(duì)象來(lái)解決.外部的接口完全沒(méi)有變化.

          維護(hù)程序,從把數(shù)組(特別是異型數(shù)組)對(duì)象化開始.

          posted @ 2009-08-20 13:43 混沌中立 閱讀(1359) | 評(píng)論 (1)編輯 收藏
            2006年7月13日
          這個(gè)小的project是前一個(gè)階段,待業(yè)在家的時(shí)候,迷戀sudoku的時(shí)候,自己寫來(lái)玩的。
          正好當(dāng)時(shí)在看Uncle Bob的《Agile Software Development: Principles, Patterns, and Practices》 (敏捷軟件開發(fā):原則、模式與實(shí)踐),所以就按照自己對(duì)書中的一些概念和方法的理解,結(jié)合自己之前的開發(fā)經(jīng)驗(yàn)寫出來(lái)一段小的代碼。

          代碼行數(shù): < 900
          類的個(gè)數(shù): 18
          抽象類的個(gè)數(shù):2
          工廠類的個(gè)數(shù):1
          包的個(gè)數(shù):5

          一些關(guān)于類和包作用的說(shuō)明:
          1.Cell:表示一個(gè)Cell,是一個(gè)游戲中的一個(gè)單元格。
          ? Cell主要由3個(gè)部分組成,Point,Value,Status.
          2.Point:表示一個(gè)坐標(biāo),主要格式為:(2,3).
          ? !!!注意:由于個(gè)人比較懶,所以開始的錯(cuò)誤被貫徹了下來(lái)。
          ? 這個(gè)錯(cuò)誤就是(2,3)表示的是由最左上的位置為坐標(biāo)原點(diǎn),第二行和第三列所確定的那個(gè)單元格。也就是縱坐標(biāo)在前,橫坐標(biāo)在后了。
          3.Value:表示一個(gè)值
          4.Status:表示Cell的狀態(tài),只有兩個(gè)狀態(tài),一個(gè)是NotSure,另一個(gè)是Sure.

          5.AbstractCells:表示一些cell的集合,主要有三個(gè)子類
          ???? BlockCells:表示一個(gè)由多個(gè)Cell組成的塊,例如一個(gè)2*2由4個(gè)Cell組成的塊,或者一個(gè)2*3由6個(gè)Cell組成的塊
          ???? HorizonCells:表示一個(gè)橫行,即:從(0,0)到(0,n)坐標(biāo)確定的所有Cell的集合。
          ???? VerticalCells:表示一個(gè)縱行,即:從(0,0)到(n,0)坐標(biāo)確定的所有Cell的集合。
          6.AbstractPolicy:就是游戲的策略。
          ?? 這個(gè)主要表示的是:4*4的游戲,還是9*9的游戲。
          ?? 可以在以后對(duì)此類進(jìn)行繼承和擴(kuò)展,例如16*16的游戲我就沒(méi)有實(shí)現(xiàn)。
          ?? 主要擴(kuò)展3個(gè)方法:
          ????????????????? 1)getValueRange,返回當(dāng)前policy的value的個(gè)數(shù)。4*4的游戲的getValueRange返回的就應(yīng)該是4。
          ??? ??? ? 2)getStep:表示當(dāng)前policy中相鄰的兩個(gè)BlockCells的坐標(biāo)差。
          ??? ??? ? 3)getIncrease:說(shuō)不明白了:)(只可意會(huì)不可言傳。)
          7.Game:進(jìn)行Policy的場(chǎng)所(我一直想拋棄這個(gè)類)
          8.TestGame:游戲運(yùn)行的地方,包括從PolicyFactory取得指定的Policy,設(shè)置輸入輸出文件的路徑。
          9.PolicyFactory:取得Policy的工廠。
          ??? getPolicy(int x) :這個(gè)方法獲得的是正方形的sudoku的策略。例如:4*4的,9*9,16*16。
          ??? getPolicy(int x, int y):這個(gè)方法獲得的是長(zhǎng)方形的Sudoku的策略。例如:9*12的。


          雖然是盡量避免bad code smell,但是由于能力有限,還是出現(xiàn)了一些不好的地方。
          例如:之間的關(guān)聯(lián)關(guān)系還是很多,而且很強(qiáng);抽象的方法和抽象類的個(gè)數(shù)偏少等等。

          里面實(shí)現(xiàn)了三個(gè)解決sudoku的方法:
          1.在一個(gè)Cell中出現(xiàn)的Value,不會(huì)在和這個(gè)Cell處在同一個(gè)AbstractCells中的所有Cell中出現(xiàn);
          2.如果一個(gè)Cell中,所有可能出現(xiàn)的Value的個(gè)數(shù)為1,那么Cell的Value必然是這個(gè)最后的Value;
          2.如果一個(gè)Value,如果在當(dāng)前AbstractCells的所有其他的Cell中都不可能出現(xiàn),那么它必然是最后一個(gè)Cell的Value。

          附件1:src code
          http://www.aygfsteel.com/Files/GandofYan/sudoku.rar
          附件2:輸入輸出文件的example
          http://www.aygfsteel.com/Files/GandofYan/temp.rar

          posted @ 2006-07-13 16:19 混沌中立 閱讀(2171) | 評(píng)論 (4)編輯 收藏
            2006年6月7日
          如同Tom DeMacro說(shuō)的:無(wú)法控制的東西就不能管理,無(wú)法測(cè)量的東西就無(wú)法控制。
          軟件的度量對(duì)于設(shè)計(jì)者和開發(fā)者非常重要,之前只是對(duì)這些有一個(gè)簡(jiǎn)單的了解。今天看來(lái),了解的還遠(yuǎn)遠(yuǎn)不夠。
          • Cyclomatic Complexity (圈復(fù)雜性)
          • Response for Class (類的響應(yīng))
          • Weighted methods per class (每個(gè)類重量方法)
          一個(gè)系統(tǒng)中的所有類的這三個(gè)度量能夠說(shuō)明這個(gè)系統(tǒng)的設(shè)計(jì)上的一些問(wèn)題(不是全部),這三個(gè)度量越大越不好。
          如果一個(gè)類這三個(gè)度量很高,證明了這個(gè)類需要重構(gòu)了。

          以第一個(gè)度量來(lái)說(shuō),有下面的一個(gè)表格:

          CC Value

          Risk

          1-10

          Low risk program

          11-20

          Moderate risk

          21-50

          High risk

          >50

          Most complex and highly unstable method


          CC數(shù)值高,可以通過(guò)減少if else(switch case也算)判斷來(lái)達(dá)到目的;
          可以通過(guò)減少類與其他類的調(diào)用來(lái)減少RFC;
          通過(guò)分割大方法和大類來(lái)達(dá)到減少WMPC.

          而Uncle Bob和Jdepend的度量標(biāo)準(zhǔn)應(yīng)該算是另一個(gè)度量系統(tǒng)。

          • 關(guān)系內(nèi)聚性(H)
          用包中的每個(gè)類平均的內(nèi)部關(guān)系數(shù)目作為包內(nèi)聚性的一種表示方式。用于表示包和它的所有類之間的關(guān)系。
          H=(R+1)/N
          R:包內(nèi)類的關(guān)系數(shù)目(與包外部的類沒(méi)有關(guān)系)
          N:包內(nèi)類的數(shù)量

          • Number of Classes (Cc)
          被分析package的具體和抽象類(和接口)的數(shù)量,用于衡量package的可擴(kuò)展性。

          • Afferent Couplings (Ca)
          依賴于被分析package的其他package的數(shù)量,用于衡量pacakge的職責(zé)。
          • Efferent Couplings (Ce)
          被分析package的類所依賴的其他package的數(shù)量,用于衡量package的獨(dú)立性。
          • Abstractness (A)
          被分析package中的抽象類和接口與所在package所有類數(shù)量的比例,取值范圍為0-1。
          A=Cc/N
          • Instability (I)
          用于衡量package的不穩(wěn)定性,取值范圍為0-1。I=0表示最穩(wěn)定,I=1表示最不穩(wěn)定。
          I=Ce/(Ce+Ca)
          • Distance (D)
          ??? ??? ? 被分析package和理想曲線A+I(xiàn)=1的垂直距離,用于衡量package在穩(wěn)定性和抽象性之間的平衡。理想??? ??? ? 的package要么完全是抽象類和穩(wěn)定(x=0,y=1),要么完全是具體類和不穩(wěn)定(x=1,y=0)。
          ??? ??? ? 取值范圍為0-1,D=0表示完全符合理想標(biāo)準(zhǔn),D=1表示package最大程度地偏離了理想標(biāo)準(zhǔn)。
          ??? ?? ?? D = |A+I-1|/0.70710678
          ??? ?? ?? 注:0.70710678*0.70710678 =2,既為“根號(hào)2“

          我認(rèn)為D是一個(gè)綜合的度量,架構(gòu)和設(shè)計(jì)的改善可以通過(guò)D數(shù)值的減少來(lái)體現(xiàn),反之就可以認(rèn)為是設(shè)計(jì)和架構(gòu)的退化。


          讀過(guò)http://javaboutique.internet.com/tutorials/metrics/index.html之后的一些想法

          另一篇中文的內(nèi)容相近的文章可以參考http://www.jdon.com/artichect/coupling.htm

          不過(guò)第二篇的中文文章中間關(guān)于Cyclomatic Complexity,有一個(gè)情況遺漏了
          public void findApplications(String id, String name){

          if(id!=null && name!=null) {
          //do something
          }else{
          //do something
          }
          }
          這種情況的CC不是2+1,而是2+1+1,依據(jù)是公式(1)。公式(2)應(yīng)該是公式(1)的簡(jiǎn)化版。
          Cyclomatic ComplexityCC) = no of decision points + no of logical operations +1        (1)

          Cyclomatic Complexity (CC) = number of decision points +1 (2)

          參考了JDepend的參數(shù)和Uncle Bob的《
          Agile Software Development: Principles, Patterns, and Practices(敏捷軟件開發(fā):原則、模式與實(shí)踐)
          posted @ 2006-06-07 10:52 混沌中立 閱讀(1521) | 評(píng)論 (3)編輯 收藏
            2006年5月30日
          轉(zhuǎn)自:http://www.keyusoft.cn/Contentview.aspx?year=2005&month=$10&day=$6&postid=123

          通過(guò)一周左右的研究,對(duì)規(guī)則引擎有了一定的了解。現(xiàn)在寫點(diǎn)東西跟大家一起交流,本文主要針對(duì)RETE算法進(jìn)行描述。我的文筆不太好,如果有什么沒(méi)講明白的或是說(shuō)錯(cuò)的地方,請(qǐng)給我留言。
          首先申明,我的帖子借鑒了網(wǎng)上很流行的一篇帖子,好像是來(lái)自CSDN;還有一點(diǎn),我不想做太多的名詞解釋,因?yàn)槲乙膊皇莻€(gè)研究很深的人,定義的不好怕被笑話。
          好現(xiàn)在我們開始。
          首先介紹一些網(wǎng)上對(duì)于規(guī)則引擎比較好的帖子。
          1、 來(lái)自JAVA視頻網(wǎng)
          http://forum.javaeye.com/viewtopic.php?t=7803&postdays=0&postorder=asc&start=0
          2、? RETE算法的最原始的描述,我不知道在哪里找到的,想要的人可以留下E-mail
          3、? CMU的一位博士生的畢業(yè)論文,個(gè)人覺得非常好,我的很多觀點(diǎn)都是來(lái)自這里的,要的人也可以給我發(fā)mail? ?mailto:ipointer@163.com
          ?
          接著統(tǒng)一一下術(shù)語(yǔ),很多資料里的術(shù)語(yǔ)都非常混亂。
          1、? facts 事實(shí),我們實(shí)現(xiàn)的時(shí)候,會(huì)有一個(gè)事實(shí)庫(kù)。用F表示。
          2、? patterns 模板,事實(shí)的一個(gè)模型,所有事實(shí)庫(kù)中的事實(shí)都必須滿足模板中的一個(gè)。用P表示。
          3、 ? conditions 條件,規(guī)則的組成部分。也必須滿足模板庫(kù)中的一條模板。用C表示。我們可以這樣理解facts、patterns、conditions之間的關(guān)系。 Patterns是一個(gè)接口,conditions則是實(shí)現(xiàn)這個(gè)接口的類,而facts是這個(gè)類的實(shí)例。
          4、? rules 規(guī)則,由一到多個(gè)條件構(gòu)成。一般用and或or連接conditions。用R表示。
          5、? actions 動(dòng)作,激活一條rule執(zhí)行的動(dòng)作。我們這里不作討論。
          6、? 還有一些術(shù)語(yǔ),如:working-memory、production-memory,跟這里的概念大同小異。
          7、? 還有一些,如:alpha-network、beta-network、join-node,我們下面會(huì)用到,先放一下,一會(huì)討論。
          ?
          引用一下網(wǎng)上很流行的例子,我覺得沒(méi)講明白,我在用我的想法解釋一下。
          ?
          假設(shè)在規(guī)則記憶中有下列三條規(guī)則
          ?
          if A(x) and B(x) and C(y) then add D(x)
          if A(x) and B(y) and D(x) then add E(x)
          if A(x) and B(x) and E(x) then delete A(x)
          ?
          RETE算法會(huì)先將規(guī)則編譯成下列的樹狀架構(gòu)排序網(wǎng)絡(luò)

          而工作記憶內(nèi)容及順序?yàn)閧A(1),A(2),B(2),B(3),B(4),C(5)},當(dāng)工作記憶依序進(jìn)入網(wǎng)絡(luò)后,會(huì)依序儲(chǔ)存在符合條件的節(jié)點(diǎn)中,直到完全符合條件的推論規(guī)則推出推論。以上述例子而言, 最后推得D(2)。
          ?
          讓我們來(lái)分析這個(gè)例子。
          ?
          模板庫(kù):(這個(gè)例子中只有一個(gè)模板,算法原描述中有不同的例子, 一般我們會(huì)用tuple,元組的形式來(lái)定義facts,patterns,condition)
          P: (?A , ?x)? 其中的A可能代表一定的操作,如例子中的A,B,C,D,E ; x代表操作的參數(shù)。看看這個(gè)模板是不是已經(jīng)可以描述所有的事實(shí)。
          ?
          條件庫(kù):(這里元組的第一項(xiàng)代表實(shí)際的操作,第二項(xiàng)代表形參)
          C1: (A , <x>)
          C2: (B , <x>)
          C3: (C , <y>)
          C4: (D , <x>)
          C5: (E , <x>)
          C6: (B , <y>)
          ?
          事實(shí)庫(kù):(第二項(xiàng)代表實(shí)參)
          F1: (A,1)
          F2: (A,2)
          F3: (B,2)
          F4: (B,3)
          F5: (B,4)
          F6: (C,5)
          ?
          ?????? 規(guī)則庫(kù):
          ?????? ? R1: c1^c2^c3
          ?????? ? R2: c1^c2^c4
          ?????? ? R3: c1^c2^c5
          ?
          ??????
          ?????? 有人可能會(huì)質(zhì)疑R1: c1^c2^c3,沒(méi)有描述出,原式中:
          if A(x) and B(x) and C(y) then add D(x),A=B的關(guān)系。但請(qǐng)仔細(xì)看一下,這一點(diǎn)已經(jīng)在條件庫(kù)中定義出來(lái)了。
          ?
          ?????? 下面我來(lái)描述一下,規(guī)則引擎中RETE算法的實(shí)現(xiàn)。
          ?????? 首先,我們要定一些規(guī)則,根據(jù)這些規(guī)則,我們的引擎可以編譯出一個(gè)樹狀結(jié)構(gòu),上面的那張圖中是一種簡(jiǎn)易的表現(xiàn),其實(shí)在實(shí)現(xiàn)的時(shí)候不是這個(gè)樣子的。
          ?????? 這就是beta-network出場(chǎng)的時(shí)候了,根據(jù)rules我們就可以確定beta-network,下面,我就畫出本例中的beta-network,為了描述方便,我把a(bǔ)lpha-network也畫出來(lái)了。
          ??????
          ?
          上圖中,左邊的部分就是beta-network,右邊是alpha-network,圓圈是join-node.
          從上圖中,我們可以驗(yàn)證,在beta-network中,表現(xiàn)出了rules的內(nèi)容,其中r1,r2,r3共享了許多BM和join-node,這是由于這些規(guī)則中有共同的部分,這樣能加快match的速度。
          右 邊的alpha-network是根據(jù)事實(shí)庫(kù)構(gòu)建的,其中除alpha-network節(jié)點(diǎn)的節(jié)點(diǎn)都是根據(jù)每一條condition,從事實(shí)庫(kù)中 match過(guò)來(lái)的,這一過(guò)程是靜態(tài)的,即在編譯構(gòu)建網(wǎng)絡(luò)的過(guò)程中已經(jīng)建立的。只要事實(shí)庫(kù)是穩(wěn)定的,即沒(méi)有大幅度的變化,RETE算法的執(zhí)行效率應(yīng)該是非常 高的,其原因就是已經(jīng)通過(guò)靜態(tài)的編譯,構(gòu)建了alpha-network。我們可以驗(yàn)證一下,滿足c1的事實(shí)確實(shí)是w1,w2。
          下 面我們就看一下,這個(gè)算法是怎么來(lái)運(yùn)行的,即怎么來(lái)確定被激活的rules的。從top-node往下遍歷,到一個(gè)join-node,與AM for c1的節(jié)點(diǎn)匯合,運(yùn)行到match c1節(jié)點(diǎn)。此時(shí),match c1節(jié)點(diǎn)的內(nèi)容就是:w1,w2。繼續(xù)往下,與AM for c2匯合(所有可能的組合應(yīng)該是w1^w3,w1^w4,w1^w5,w2^w3,w2^w4,w2^w5),因?yàn)閏1^c2要求參數(shù)相同,因此, match c1^c2的內(nèi)容是:w2^w3。再繼續(xù),這里有一個(gè)扇出(fan-out),其中只有一個(gè)join-node可以被激活,因?yàn)榕赃叺腁M只有一個(gè)非空。 因此,也只有R1被激活了。
          解決扇出帶來(lái)的效率降低的問(wèn)題,我們可以使用hashtable來(lái)解決這個(gè)問(wèn)題。
          RETE算法還有一些問(wèn)題,如:facts庫(kù)變化,我們?cè)趺床拍芨咝У闹亟╝lpha-network,同理包括rules的變化對(duì)beta-network的影響。這一部分我還沒(méi)細(xì)看,到時(shí)候再貼出來(lái)吧。
          posted @ 2006-05-30 15:30 混沌中立 閱讀(1059) | 評(píng)論 (2)編輯 收藏
          最近插件又加多了,eclipse老是死掉
          ?
          一怒之下,刪除重裝
          ?
          以前因?yàn)閼?沒(méi)有把插件的目錄和主體的目錄分開,這次也給它分開了
          ?
          ?
          插件少了之后,eclipse確實(shí)快了不少
          ?
          附eclipse啟動(dòng)參數(shù): -nl en_US vmargs -Xverify:none -Xms256M -Xmx1024M -XX:PermSize=50M? -XX:+UseParallelGC
          posted @ 2006-05-30 13:48 混沌中立 閱讀(576) | 評(píng)論 (0)編輯 收藏
          freemind一個(gè)比較不錯(cuò)free的 mind map 軟件,很多人建議使用這個(gè)來(lái)管理自己的思路.
          ?
          mind manager另一個(gè)比較不錯(cuò)的mind map的軟件,可以和office兼容.不過(guò)是商業(yè)的
          ?
          visio,就不介紹了.office里面有的東西.做流程圖來(lái)說(shuō),確實(shí)是比較好的軟件.但是在思路不清楚的時(shí)候,很難畫出什么有用的東西來(lái).這點(diǎn)就比不上前面兩個(gè)東西了.不過(guò)對(duì)我來(lái)說(shuō)visio可能更順手,因?yàn)槲医?jīng)常畫的是軟件流程圖........
          posted @ 2006-05-30 13:36 混沌中立 閱讀(1766) | 評(píng)論 (2)編輯 收藏
          總結(jié)一下最近關(guān)于domain object以及相關(guān)的討論
          ?
          在最近的圍繞domain object的討論中浮現(xiàn)出來(lái)了三種模型,(還有一些其他的旁枝,不一一分析了),經(jīng)過(guò)一番討論,各種問(wèn)題逐漸清晰起來(lái),在這里我試圖做一個(gè)總結(jié),便于大家了解和掌握。

          第一種模型:只有g(shù)etter/setter方法的純數(shù)據(jù)類,所有的業(yè)務(wù)邏輯完全由business object來(lái)完成(又稱TransactionScript),這種模型下的domain object被Martin Fowler稱之為“貧血的domain object”。下面用舉一個(gè)具體的代碼來(lái)說(shuō)明,代碼來(lái)自Hibernate的caveatemptor,但經(jīng)過(guò)我的改寫:

          一個(gè)實(shí)體類叫做Item,指的是一個(gè)拍賣項(xiàng)目
          一個(gè)DAO接口類叫做ItemDao
          一個(gè)DAO接口實(shí)現(xiàn)類叫做ItemDaoHibernateImpl
          一個(gè)業(yè)務(wù)邏輯類叫做ItemManager(或者叫做ItemService)

          java代碼:?

          public class Item implementsSerializable{
          ? ? privateLong id = null;
          ? ? privateint version;
          ? ? privateString name;
          ? ? private User seller;
          ? ? privateString description;
          ? ? private MonetaryAmount initialPrice;
          ? ? private MonetaryAmount reservePrice;
          ? ? privateDate startDate;
          ? ? privateDate endDate;
          ? ? privateSet categorizedItems = newHashSet();
          ? ? privateCollection bids = newArrayList();
          ? ? private Bid successfulBid;
          ? ? private ItemState state;
          ? ? private User approvedBy;
          ? ? privateDate approvalDatetime;
          ? ? privateDate created = newDate();
          ? ? //? getter/setter方法省略不寫,避免篇幅太長(zhǎng)
          }



          java代碼:?

          public interface ItemDao {
          ? ? public Item getItemById(Long id);
          ? ? publicCollection findAll();
          ? ? publicvoid updateItem(Item item);
          }



          ItemDao定義持久化操作的接口,用于隔離持久化代碼。

          java代碼:?

          public class ItemDaoHibernateImpl implements ItemDao extends HibernateDaoSupport {
          ? ? public Item getItemById(Long id){
          ? ? ? ? return(Item) getHibernateTemplate().load(Item.class, id);
          ? ? }
          ? ? publicCollection findAll(){
          ? ? ? ? return(List) getHibernateTemplate().find("from Item");
          ? ? }
          ? ? publicvoid updateItem(Item item){
          ? ? ? ? getHibernateTemplate().update(item);
          ? ? }
          }


          ItemDaoHibernateImpl完成具體的持久化工作,請(qǐng)注意,數(shù)據(jù)庫(kù)資源的獲取和釋放是在ItemDaoHibernateImpl 里面處理的,每個(gè)DAO方法調(diào)用之前打開Session,DAO方法調(diào)用之后,關(guān)閉Session。(Session放在ThreadLocal中,保證一次調(diào)用只打開關(guān)閉一次)

          java代碼:?

          public class ItemManager {
          ? ? private ItemDao itemDao;
          ? ? publicvoid setItemDao(ItemDao itemDao){ this.itemDao = itemDao;}
          ? ? public Bid loadItemById(Long id){
          ? ? ? ? itemDao.loadItemById(id);
          ? ? }
          ? ? publicCollection listAllItems(){
          ? ? ? ? return? itemDao.findAll();
          ? ? }
          ? ? public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid)throws BusinessException {
          ? ? ? ? ? ? if(currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0){
          ? ? ? ? ? ? throw new BusinessException("Bid too low.");
          ? ? }
          ? ?
          ? ? // Auction is active
          ? ? if( !state.equals(ItemState.ACTIVE))
          ? ? ? ? ? ? throw new BusinessException("Auction is not active yet.");
          ? ?
          ? ? // Auction still valid
          ? ? if( item.getEndDate().before(newDate()))
          ? ? ? ? ? ? throw new BusinessException("Can't place new bid, auction already ended.");
          ? ?
          ? ? // Create new Bid
          ? ? Bid newBid = new Bid(bidAmount, item, bidder);
          ? ?
          ? ? // Place bid for this Item
          ? ? item.getBids().add(newBid);
          ? ? itemDao.update(item);? ? ?//? 調(diào)用DAO完成持久化操作
          ? ? return newBid;
          ? ? }
          }



          事務(wù)的管理是在ItemManger這一層完成的,ItemManager實(shí)現(xiàn)具體的業(yè)務(wù)邏輯。除了常見的和CRUD有關(guān)的簡(jiǎn)單邏輯之外,這里還有一個(gè)placeBid的邏輯,即項(xiàng)目的競(jìng)標(biāo)。

          以上是一個(gè)完整的第一種模型的示例代碼。在這個(gè)示例中,placeBid,loadItemById,findAll等等業(yè)務(wù)邏輯統(tǒng)統(tǒng)放在ItemManager中實(shí)現(xiàn),而Item只有g(shù)etter/setter方法。

          ?

          ?

          第二種模型,也就是Martin Fowler指的rich domain object是下面這樣子的:

          一個(gè)帶有業(yè)務(wù)邏輯的實(shí)體類,即domain object是Item
          一個(gè)DAO接口ItemDao
          一個(gè)DAO實(shí)現(xiàn)ItemDaoHibernateImpl
          一個(gè)業(yè)務(wù)邏輯對(duì)象ItemManager

          java代碼:?

          public class Item implementsSerializable{
          ? ? //? 所有的屬性和getter/setter方法同上,省略
          ? ? public Bid placeBid(User bidder, MonetaryAmount bidAmount,
          ? ? ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid)
          ? ? ? ? ? ? throws BusinessException {
          ? ?
          ? ? ? ? ? ? // Check highest bid (can also be a different Strategy (pattern))
          ? ? ? ? ? ? if(currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0){
          ? ? ? ? ? ? ? ? ? ? throw new BusinessException("Bid too low.");
          ? ? ? ? ? ? }
          ? ?
          ? ? ? ? ? ? // Auction is active
          ? ? ? ? ? ? if( !state.equals(ItemState.ACTIVE))
          ? ? ? ? ? ? ? ? ? ? throw new BusinessException("Auction is not active yet.");
          ? ?
          ? ? ? ? ? ? // Auction still valid
          ? ? ? ? ? ? if( this.getEndDate().before(newDate()))
          ? ? ? ? ? ? ? ? ? ? throw new BusinessException("Can't place new bid, auction already ended.");
          ? ?
          ? ? ? ? ? ? // Create new Bid
          ? ? ? ? ? ? Bid newBid = new Bid(bidAmount, this, bidder);
          ? ?
          ? ? ? ? ? ? // Place bid for this Item
          ? ? ? ? ? ? this.getBids.add(newBid);? // 請(qǐng)注意這一句,透明的進(jìn)行了持久化,但是不能在這里調(diào)用ItemDao,Item不能對(duì)ItemDao產(chǎn)生依賴!
          ? ?
          ? ? ? ? ? ? return newBid;
          ? ? }
          }



          競(jìng)標(biāo)這個(gè)業(yè)務(wù)邏輯被放入到Item中來(lái)。請(qǐng)注意this.getBids.add(newBid); 如果沒(méi)有Hibernate或者JDO這種O/R Mapping的支持,我們是無(wú)法實(shí)現(xiàn)這種透明的持久化行為的。但是請(qǐng)注意,Item里面不能去調(diào)用ItemDAO,對(duì)ItemDAO產(chǎn)生依賴!

          ItemDao和ItemDaoHibernateImpl的代碼同上,省略。

          java代碼:?

          public class ItemManager {
          ? ? private ItemDao itemDao;
          ? ? publicvoid setItemDao(ItemDao itemDao){ this.itemDao = itemDao;}
          ? ? public Bid loadItemById(Long id){
          ? ? ? ? itemDao.loadItemById(id);
          ? ? }
          ? ? publicCollection listAllItems(){
          ? ? ? ? return? itemDao.findAll();
          ? ? }
          ? ? public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid)throws BusinessException {
          ? ? ? ? item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid);
          ? ? ? ? itemDao.update(item);? ? // 必須顯式的調(diào)用DAO,保持持久化
          ? ? }
          }



          在第二種模型中,placeBid業(yè)務(wù)邏輯是放在Item中實(shí)現(xiàn)的,而loadItemById和findAll業(yè)務(wù)邏輯是放在 ItemManager中實(shí)現(xiàn)的。不過(guò)值得注意的是,即使placeBid業(yè)務(wù)邏輯放在Item中,你仍然需要在ItemManager中簡(jiǎn)單的封裝一層,以保證對(duì)placeBid業(yè)務(wù)邏輯進(jìn)行事務(wù)的管理和持久化的觸發(fā)。

          這種模型是Martin Fowler所指的真正的domain model。在這種模型中,有三個(gè)業(yè)務(wù)邏輯方法:placeBid,loadItemById和findAll,現(xiàn)在的問(wèn)題是哪個(gè)邏輯應(yīng)該放在Item 中,哪個(gè)邏輯應(yīng)該放在ItemManager中。在我們這個(gè)例子中,placeBid放在Item中(但是ItemManager也需要對(duì)它進(jìn)行簡(jiǎn)單的封裝),loadItemById和findAll是放在ItemManager中的。

          切分的原則是什么呢? Rod Johnson提出原則是“case by case”,可重用度高的,和domain object狀態(tài)密切關(guān)聯(lián)的放在Item中,可重用度低的,和domain object狀態(tài)沒(méi)有密切關(guān)聯(lián)的放在ItemManager中。

          我提出的原則是:看業(yè)務(wù)方法是否顯式的依賴持久化。

          Item的placeBid這個(gè)業(yè)務(wù)邏輯方法沒(méi)有顯式的對(duì)持久化ItemDao接口產(chǎn)生依賴,所以要放在Item中。請(qǐng)注意,如果脫離了Hibernate這個(gè)持久化框架,Item這個(gè)domain object是可以進(jìn)行單元測(cè)試的,他不依賴于Hibernate的持久化機(jī)制。它是一個(gè)獨(dú)立的,可移植的,完整的,自包含的域?qū)ο?/span>。

          而loadItemById和findAll這兩個(gè)業(yè)務(wù)邏輯方法是必須顯式的對(duì)持久化ItemDao接口產(chǎn)生依賴,否則這個(gè)業(yè)務(wù)邏輯就無(wú)法完成。如果你要把這兩個(gè)方法放在Item中,那么Item就無(wú)法脫離Hibernate框架,無(wú)法在Hibernate框架之外獨(dú)立存在。

          ?

          ?

          第三種模型印象中好像是firebody或者是Archie提出的(也有可能不是,記不清楚了),簡(jiǎn)單的來(lái)說(shuō),這種模型就是把第二種模型的domain object和business object合二為一了。所以ItemManager就不需要了,在這種模型下面,只有三個(gè)類,他們分別是:

          Item:包含了實(shí)體類信息,也包含了所有的業(yè)務(wù)邏輯
          ItemDao:持久化DAO接口類
          ItemDaoHibernateImpl:DAO接口的實(shí)現(xiàn)類

          由于ItemDao和ItemDaoHibernateImpl和上面完全相同,就省略了。

          java代碼:?

          public class Item implementsSerializable{
          ? ? //? 所有的屬性和getter/setter方法都省略
          ? ?privatestatic ItemDao itemDao;
          ? ? publicvoid setItemDao(ItemDao itemDao){this.itemDao = itemDao;}
          ? ?
          ? ? publicstatic Item loadItemById(Long id){
          ? ? ? ? return(Item) itemDao.loadItemById(id);
          ? ? }
          ? ? publicstaticCollection findAll(){
          ? ? ? ? return(List) itemDao.findAll();
          ? ? }

          ? ? public Bid placeBid(User bidder, MonetaryAmount bidAmount,
          ? ? ? ? ? ? ? ? ? ? Bid currentMaxBid, Bid currentMinBid)
          ? ? throws BusinessException {
          ? ?
          ? ? ? ? // Check highest bid (can also be a different Strategy (pattern))
          ? ? ? ? if(currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0){
          ? ? ? ? ? ? ? ? throw new BusinessException("Bid too low.");
          ? ? ? ? }
          ? ? ? ?
          ? ? ? ? // Auction is active
          ? ? ? ? if( !state.equals(ItemState.ACTIVE))
          ? ? ? ? ? ? ? ? throw new BusinessException("Auction is not active yet.");
          ? ? ? ?
          ? ? ? ? // Auction still valid
          ? ? ? ? if( this.getEndDate().before(newDate()))
          ? ? ? ? ? ? ? ? throw new BusinessException("Can't place new bid, auction already ended.");
          ? ? ? ?
          ? ? ? ? // Create new Bid
          ? ? ? ? Bid newBid = new Bid(bidAmount, this, bidder);
          ? ? ? ?
          ? ? ? ? // Place bid for this Item
          ? ? ? ? this.addBid(newBid);
          ? ? ? ? itemDao.update(this);? ? ? //? 調(diào)用DAO進(jìn)行顯式持久化
          ? ? ? ? return newBid;
          ? ? }
          }



          在這種模型中,所有的業(yè)務(wù)邏輯全部都在Item中,事務(wù)管理也在Item中實(shí)現(xiàn)。

          ?

          ?

          在上面三種模型之外,還有很多這三種模型的變種,例如partech的模型就是把第二種模型中的DAO和 Manager三個(gè)類合并為一個(gè)類后形成的模型;例如frain....(id很長(zhǎng)記不住)的模型就是把第三種模型的三個(gè)類完全合并為一個(gè)單類后形成的模型;例如Archie是把第三種模型的Item又分出來(lái)一些純數(shù)據(jù)類(可能是,不確定)形成的一個(gè)模型。

          但是不管怎么變,基本模型歸納起來(lái)就是上面的三種模型,下面分別簡(jiǎn)單評(píng)價(jià)一下:

          第一種模型絕大多數(shù)人都反對(duì),因此反對(duì)理由我也不多講了。但遺憾的是,我觀察到的實(shí)際情形是,很多使用Hibernate的公司最后都是這種模型,這里面有很大的原因是很多公司的技術(shù)水平?jīng)]有達(dá)到這種層次,所以導(dǎo)致了這種貧血模型的出現(xiàn)。從這一點(diǎn)來(lái)說(shuō),Martin Fowler的批評(píng)聲音不是太響了,而是太弱了,還需要再繼續(xù)吶喊。

          第二種模型就是Martin Fowler一直主張的模型,實(shí)際上也是我一直在實(shí)際項(xiàng)目中采用這種模型。我沒(méi)有看過(guò)Martin的POEAA,之所以能夠自己摸索到這種模型,也是因?yàn)閺?2年我已經(jīng)開始思考這個(gè)問(wèn)題并且尋求解決方案了,但是當(dāng)時(shí)沒(méi)有看到Hibernate,那時(shí)候做的一個(gè)小型項(xiàng)目我已經(jīng)按照這種模型來(lái)做了,但是由于沒(méi)有O/R Mapping的支持,寫到后來(lái)又不得不全部改成貧血的domain object,項(xiàng)目做完以后再繼續(xù)找,隨后就發(fā)現(xiàn)了Hibernate。當(dāng)然,現(xiàn)在很多人一開始就是用Hibernate做項(xiàng)目,沒(méi)有經(jīng)歷過(guò)我經(jīng)歷的那個(gè)階段。

          不過(guò)我覺得這種模型仍然不夠完美,因?yàn)槟氵€是需要一個(gè)業(yè)務(wù)邏輯層來(lái)封裝所有的domain logic,這顯得非常羅嗦,并且業(yè)務(wù)邏輯對(duì)象的接口也不夠穩(wěn)定。如果不考慮業(yè)務(wù)邏輯對(duì)象的重用性的話(業(yè)務(wù)邏輯對(duì)象的可重用性也不可能好),很多人干脆就去掉了xxxManager這一層,在Web層的Action代碼直接調(diào)用xxxDao,同時(shí)容器事務(wù)管理配置到Action這一層上來(lái)。 Hibernate的caveatemptor就是這樣架構(gòu)的一個(gè)典型應(yīng)用。

          第三種模型是我很反對(duì)的一種模型,這種模型下面,Domain Object和DAO形成了雙向依賴關(guān)系,無(wú)法脫離框架測(cè)試,并且業(yè)務(wù)邏輯層的服務(wù)也和持久層對(duì)象的狀態(tài)耦合到了一起,會(huì)造成程序的高度的復(fù)雜性,很差的靈活性和糟糕的可維護(hù)性。也許將來(lái)技術(shù)進(jìn)步導(dǎo)致的O/R Mapping管理下的domain object發(fā)展到足夠的動(dòng)態(tài)持久透明化的話,這種模型才會(huì)成為一個(gè)理想的選擇。就像O/R Mapping的流行使得第二種模型成為了可能(O/R Mapping流行以前,我們只能用第一種模型,第二種模型那時(shí)候是不現(xiàn)實(shí)的)。

          ?

          ?

          既然大家都統(tǒng)一了觀點(diǎn),那么就有了一個(gè)很好的討論問(wèn)題的基礎(chǔ)了。Martin Fowler的Domain Model,或者說(shuō)我們的第二種模型難道是完美無(wú)缺的嗎?當(dāng)然不是,接下來(lái)我就要分析一下它的不足,以及可能的解決辦法,而這些都來(lái)源于我個(gè)人的實(shí)踐探索。

          在第二種模型中,我們可以清楚的把這4個(gè)類分為三層:

          1、實(shí)體類層,即Item,帶有domain logic的domain object
          2、DAO層,即ItemDao和ItemDaoHibernateImpl,抽象持久化操作的接口和實(shí)現(xiàn)類
          3、業(yè)務(wù)邏輯層,即ItemManager,接受容器事務(wù)控制,向Web層提供統(tǒng)一的服務(wù)調(diào)用

          在這三層中我們大家可以看到,domain object和DAO都是非常穩(wěn)定的層,其實(shí)原因也很簡(jiǎn)單,因?yàn)閐omain object是映射數(shù)據(jù)庫(kù)字段的,數(shù)據(jù)庫(kù)字段不會(huì)頻繁變動(dòng),所以domain object也相對(duì)穩(wěn)定,而面向數(shù)據(jù)庫(kù)持久化編程的DAO層也不過(guò)就是CRUD而已,不會(huì)有更多的花樣,所以也很穩(wěn)定。

          問(wèn)題就在于這個(gè)充當(dāng)business workflow facade的業(yè)務(wù)邏輯對(duì)象,它的變動(dòng)是相當(dāng)頻繁的。業(yè)務(wù)邏輯對(duì)象通常都是無(wú)狀態(tài)的、受事務(wù)控制的、Singleton類,我們可以考察一下業(yè)務(wù)邏輯對(duì)象都有哪幾類業(yè)務(wù)邏輯方法:

          第一類:DAO接口方法的代理,就是上面例子中的loadItemById方法和findAll方法。

          ItemManager之所以要代理這種類,目的有兩個(gè):向Web層提供統(tǒng)一的服務(wù)調(diào)用入口點(diǎn)和給持久化方法增加事務(wù)控制功能。這兩點(diǎn)都很容易理解,你不能既給Web層程序員提供xxxManager,也給他提供xxxDao,所以你需要用xxxManager封裝xxxDao,在這里,充當(dāng)了一個(gè)簡(jiǎn)單代理功能;而事務(wù)控制也是持久化方法必須的,事務(wù)可能需要跨越多個(gè)DAO方法調(diào)用,所以必須放在業(yè)務(wù)邏輯層,而不能放在DAO層。

          但是必須看到,對(duì)于一個(gè)典型的web應(yīng)用來(lái)說(shuō),絕大多數(shù)的業(yè)務(wù)邏輯都是簡(jiǎn)單的CRUD邏輯,所以這種情況下,針對(duì)每個(gè)DAO方法,xxxManager都需要提供一個(gè)對(duì)應(yīng)的封裝方法,這不但是非常枯燥的,也是令人感覺非常不好的。


          第二類:domain logic的方法代理。就是上面例子中placeBid方法。雖然Item已經(jīng)有了placeBid方法,但是ItemManager仍然需要封裝一下Item的placeBid,然后再提供一個(gè)簡(jiǎn)單封裝之后的代理方法。

          這和第一種情況類似,其原因也一樣,也是為了給Web層提供一個(gè)統(tǒng)一的服務(wù)調(diào)用入口點(diǎn)和給隱式的持久化動(dòng)作提供事務(wù)控制。

          同樣,和第一種情況一樣,針對(duì)每個(gè)domain logic方法,xxxManager都需要提供一個(gè)對(duì)應(yīng)的封裝方法,同樣是枯燥的,令人不爽的。


          第三類:需要多個(gè)domain object和DAO參與協(xié)作的business workflow。這種情況是業(yè)務(wù)邏輯對(duì)象真正應(yīng)該完成的職責(zé)。

          在這個(gè)簡(jiǎn)單的例子中,沒(méi)有涉及到這種情況,不過(guò)大家都可以想像的出來(lái)這種應(yīng)用場(chǎng)景,因此不必舉例說(shuō)明了。

          通過(guò)上面的分析可以看出,只有第三類業(yè)務(wù)邏輯方法才是業(yè)務(wù)邏輯對(duì)象真正應(yīng)該承擔(dān)的職責(zé),而前兩類業(yè)務(wù)邏輯方法都是“無(wú)奈之舉”,不得不為之的事情,不但枯燥,而且令人沮喪。




          分析完了業(yè)務(wù)邏輯對(duì)象,我們?cè)倩仡^看一下domain object,我們要仔細(xì)考察一下domain logic的話,會(huì)發(fā)現(xiàn)domain logic也分為兩類:

          第一類:需要持久層框架隱式的實(shí)現(xiàn)透明持久化的domain logic,例如Item的placeBid方法中的這一句:

          java代碼:?

          this.getBids().add(newBid);


          上面已經(jīng)著重提到,雖然這僅僅只是一個(gè)Java集合的添加新元素的操作,但是實(shí)際上通過(guò)事務(wù)的控制,會(huì)潛在的觸發(fā)兩條SQL:一條是insert一條記錄到bid表,一條是更新item表相應(yīng)的記錄。如果我們讓Item脫離Hibernate進(jìn)行單元測(cè)試,它就是一個(gè)單純的Java集合操作,如果我們把他加入到Hibernate框架中,他就會(huì)潛在的觸發(fā)兩條SQL,這就是隱式的依賴于持久化的domain logic
          特別請(qǐng)注意的一點(diǎn)是:在沒(méi)有Hibernate/JDO這類可以實(shí)現(xiàn)“透明的持久化”工具出現(xiàn)之前,這類domain logic是無(wú)法實(shí)現(xiàn)的。

          對(duì)于這一類domain logic,業(yè)務(wù)邏輯對(duì)象必須提供相應(yīng)的封裝方法,以實(shí)現(xiàn)事務(wù)控制。


          第二類:完全不依賴持久化的domain logic,例如readonly例子中的Topic,如下:

          java代碼:?

          class Topic {
          ? ? boolean isAllowReply(){
          ? ? ? ? Calendar dueDate = Calendar.getInstance();
          ? ? ? ? dueDate.setTime(lastUpdatedTime);
          ? ? ? ? dueDate.add(Calendar.DATE, forum.timeToLive);
          ? ?
          ? ? ? ? Date now = newDate();
          ? ? ? ? return now.after(dueDate.getTime());
          ? ? }
          }



          注意這個(gè)isAllowReply方法,他和持久化完全不發(fā)生一丁點(diǎn)關(guān)系。在實(shí)際的開發(fā)中,我們同樣會(huì)遇到很多這種不需要持久化的業(yè)務(wù)邏輯(主要發(fā)生在日期運(yùn)算、數(shù)值運(yùn)算和枚舉運(yùn)算方面),這種domain logic不管脫離不脫離所在的框架,它的行為都是一致的。對(duì)于這種domain logic,業(yè)務(wù)邏輯層并不需要提供封裝方法,它可以適用于任何場(chǎng)合。
          posted @ 2006-05-30 13:31 混沌中立 閱讀(3071) | 評(píng)論 (1)編輯 收藏

          原文地址:http://www.cnblogs.com/idior/archive/2005/07/04/186086.html

          近日?有關(guān)o/r?m的討論突然多了起來(lái).?在這里覺得有必要澄清一些概念,?免的大家討論來(lái)討論去,?才發(fā)現(xiàn)最根本的理解有問(wèn)題.

          1.?何謂實(shí)體?
          實(shí)體(類似于j2ee中的Entity?Bean)通常指一個(gè)承載數(shù)據(jù)的對(duì)象,?但是注意它也是可以有行為的!?只不過(guò)它的行為一般只操作自身的數(shù)據(jù).?比如下面這個(gè)例子:


          class?Person
          {
          ??string?firstName;
          ??string?lastName;

          ??public?void?GetName()
          ??{
          ?????return??lastName+firstName;
          ??}???
          }


          GetName就是它的一個(gè)行為.

          2?何謂對(duì)象?
          對(duì)象最重要的特性在于它擁有行為.?僅僅擁有數(shù)據(jù),你可以稱它為對(duì)象,?但是它卻失去它最重要的靈魂.?


          class?Person
          {
          ??string?firstName;
          ??string?lastName;
          ??Role?role;
          ??int?baseWage;
          ??public?void?GetSalary()
          ??{
          ?????return?baseWage*role.GetFactory();
          ??}???
          }

          這樣需要和別的對(duì)象(不是Value?Object)打交道的對(duì)象,我就不再稱其為實(shí)體.?領(lǐng)域模型就是指由這些具有業(yè)務(wù)邏輯的對(duì)象構(gòu)成的模型.

          3.?E/R?M?or?O/R?M?!!
          仔細(xì)想想我們?yōu)槭裁葱枰猳/r?m,無(wú)非是想利用oo的多態(tài)來(lái)處理復(fù)雜的業(yè)務(wù)邏輯,?而不是靠一堆的if?else.?
          而現(xiàn)在在很多人的手上o/r?m全變成了e/r?m.他們不考慮對(duì)象的行為,?而全關(guān)注于如何保存數(shù)據(jù).這樣也難怪他們會(huì)產(chǎn)生將CRUD這些操作放入對(duì)象中的念頭.?如果你不能深刻理解oo,?那么我不推薦你使用o/r?m,?Table?Gateway,?Row?Gateway才是你想要的東西.


          作為一個(gè)O/R?M框架,很重要的一點(diǎn)就是實(shí)現(xiàn)映射的透明性(Transparent),比較顯著的特點(diǎn)就是在代碼中我們是看不到SQL語(yǔ)句的(框架自動(dòng)生成了)。這里所指的O/R?M就是類似于此的框架,

          4.?POEAA中的相關(guān)概念
          ??很多次發(fā)現(xiàn)有人錯(cuò)用其中的概念,?這里順便總結(jié)一下:
          ??1.?Table?Gateway
          ????以表為單位的實(shí)體,基本沒(méi)有行為,只有CRUD操作.
          ??2.?Row?Gateway
          ????以行為單位的實(shí)體,基本沒(méi)有行為,只有CRUD操作.
          ??3.?Active?Record
          ????以行為單位的實(shí)體,擁有一些基本的操作自身數(shù)據(jù)的行為(如上例中的GetName),同時(shí)包含有CRUD操作.
          其實(shí)Active?Record最符合某些簡(jiǎn)單的需求,?接近于E/R?m.
          通常也有很多人把它當(dāng)作O/R?m.不過(guò)需要注意的是Active?Record中是充滿了SQL語(yǔ)句的(不像orm的SQL透明),?所以有人想起來(lái)利用O/R?m來(lái)實(shí)現(xiàn)"Active?Record",?雖然在他們眼里看起來(lái)很方便,?其實(shí)根本就是返祖.
          用CodeGenerator來(lái)實(shí)現(xiàn)Active?Record也許是一個(gè)比較好的方法.
          ??4.?Data?Mapper
          這才是真正的O/R?m,Hibernate等等工具的目標(biāo).

          5.O/R?M需要關(guān)注的地方?(希望大家?guī)兔ν晟埔幌?
          ?1.?關(guān)聯(lián),?O/R?M是如何管理類之間的關(guān)聯(lián).當(dāng)然這不僅于o/r?m有關(guān)與設(shè)計(jì)者的設(shè)計(jì)水平也有很大關(guān)系.
          ?2.?O/R?M對(duì)繼承關(guān)系的處理.
          ?3.?O/R?M對(duì)事務(wù)的支持.
          ?4.?O/R?M對(duì)查詢的支持.
          ?
          以上觀點(diǎn)僅屬個(gè)人意見,?不過(guò)在大家討論有關(guān)O/R?m之前,?希望先就一些基本概念達(dá)成共識(shí),?不然討論下去會(huì)越離越遠(yuǎn).?


          (建議:?如果對(duì)oo以及dp沒(méi)有一定程度的了解,?最好別使用o/r?m,?dataset?加上codesmith或許是更好的選擇)
          posted @ 2006-05-30 13:20 混沌中立 閱讀(387) | 評(píng)論 (0)編輯 收藏
          僅列出標(biāo)題  下一頁(yè)
          主站蜘蛛池模板: 吉水县| 简阳市| 莱州市| 台东市| 双桥区| 北票市| 休宁县| 南木林县| 呼和浩特市| 洱源县| 隆化县| 遂川县| 常州市| 鄄城县| 池州市| 阜宁县| 井冈山市| 永泰县| 安泽县| 手机| 延庆县| 台北市| 葵青区| 合江县| 南陵县| 丁青县| 孝昌县| 涿州市| 小金县| 松阳县| 锦屏县| 桑植县| 皋兰县| 莱州市| 南溪县| 隆德县| 灯塔市| 固始县| 阳谷县| 内乡县| 新田县|