
2006年3月17日
在一個項目里面有這么一個技術需求:
1.集合中元素個數,10M
2.根據上限和下限從一個Set中過濾出滿足要求的元素集合.
實際這個是個很典型的技術要求, 之前的項目也遇見過,但是因為當時的類庫不多, 都是直接手寫實現的. 方式基本等同于第一個方式.
在這個過程中, 我寫了四個方式, 基本記錄到下面.
第一個方式:對Set進行迭代器遍歷, 判斷每個元素是否都在上限和下限范圍中.如果滿足則添加到結果集合中, 最后返回結果集合.
測試效果:集合大小100K, 運算時間 3000ms+
過濾部分的邏輯如下:
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 }
第二個方式: 借助TreeSet, 原始集合進行排序, 然后直接subset.
測試效果: 集合大小10M, 運算時間: 12000ms+(獲得TreeSet) , 200ms(獲得結果)
過濾部分的邏輯如下(非常繁瑣):
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, 直接對于原始Set進行filter.
測試效果:集合大小10M,過濾結果1M, 運算時間: 1000ms+
過濾部分的代碼如下:
1 //過濾的主體邏輯
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), 直接對于原始Set進行Filter
測試效果:集合大小10M,過濾結果1M, 運算時間: 100ms-
過濾部分的代碼如下:
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 }
四種方式對比如下:
第一種方式: 僅依賴于JAVA原生類庫 遍歷時間最慢, 代碼量很小
第二種方式: 僅依賴于JAVA原生類庫 遍歷時間比較慢(主要慢在生成有序Set), 代碼量最多
第三種方式: 依賴于Apache Commons Collections, 遍歷時間比較快, 代碼量很少
第四種方式: 依賴于Guava, 遍歷時間最快, 代碼量很少
基于目前個人的技術水平和視野, 第四種方式可能是最佳選擇.
記錄一下, 以后可能還會有更好的方案.
posted @
2014-06-21 23:33 混沌中立 閱讀(7370) |
評論 (10) |
編輯 收藏
在幾年之前,在大學里面的時候,認為系統的架構設計,就像建筑設計一樣,會把骨架搭成,然后有具體人員進行詳細的開發.
在后來,工作中,慢慢有了一些變化,因為原先的想法不太切合實際,系統就是在變化之中的,如果固定了骨架,那就很難的敏捷面對變化.
所以,系統的架構設計,應該是面向接口的設計,確定各個部分之間的數據接口和方法接口.這樣,即使有了變化,只要遵循接口的定義,就還是可以面對變化.
最近,又有了想法的變化.架構的設計,應該就是規則和規約的設計.設計出一系列,統一的,和諧的規則,在這些規則之前圈住的部分,實際就是系統的全貌.
接口的設計,實際只是規則和規約設計的一個部分.
架構的設計,不應該只是程序方面的事情,同時也包含了心理學方面和社會學方面的一些規則.例如:團隊在面對變化時候,需要采用的規則和流程.
只有包含了這些非程序上的規則之后,才能保證架構風格的統一協調.
以上,是我對系統設計的想法的轉變過程.記錄于此,以供回溯.
posted @
2009-09-02 10:53 混沌中立 閱讀(247) |
評論 (0) |
編輯 收藏
面對著滿屏幕的程序
是三年前,項目剛剛啟動的時候,同事寫的代碼.
三年過去了,項目由第一期變成了第七期.
這段代碼還是在這里,有個屬性是list,其中每個cell都是一個長度18的String數組.
數組里面放置了所需要導出到頁面table的內容.
現在要開始修改了,需要向頁面的table中增加4列.
繁瑣的讓人要命的工作,需要跟蹤這個循環,判斷每個pattern下面,這個長度18的數組里面放了哪些內容.
好吧,對象化維護從數組開始,把數組對折,因為這個數組時一個比較數組,前面9個元素是之前的情況,后面9個事之后的情況.
用一個bean,放入兩次就可以了.但是bean中,需要一個標志,標識是之前的情況還是之后的情況.
同時需要一個transform方法,把之前從幾個來源過來的情況,變成bean的屬性.
接下來需要一個values方法,把bean里面的屬性直接按順序轉化成數組.
本期新增的4個屬性,直接放入bean中就可以了.
這樣原來很復雜的數組,就可以簡單的用對象來解決.外部的接口完全沒有變化.
維護程序,從把數組(特別是異型數組)對象化開始.
posted @
2009-08-20 13:43 混沌中立 閱讀(1353) |
評論 (1) |
編輯 收藏
這個小的project是前一個階段,待業在家的時候,迷戀sudoku的時候,自己寫來玩的。
正好當時在看Uncle Bob的《Agile Software Development: Principles, Patterns, and Practices》 (敏捷軟件開發:原則、模式與實踐),所以就按照自己對書中的一些概念和方法的理解,結合自己之前的開發經驗寫出來一段小的代碼。
代碼行數: < 900
類的個數: 18
抽象類的個數:2
工廠類的個數:1
包的個數:5
一些關于類和包作用的說明:
1.Cell:表示一個Cell,是一個游戲中的一個單元格。
? Cell主要由3個部分組成,Point,Value,Status.
2.Point:表示一個坐標,主要格式為:(2,3).
? !!!注意:由于個人比較懶,所以開始的錯誤被貫徹了下來。
? 這個錯誤就是(2,3)表示的是由最左上的位置為坐標原點,第二行和第三列所確定的那個單元格。也就是縱坐標在前,橫坐標在后了。
3.Value:表示一個值
4.Status:表示Cell的狀態,只有兩個狀態,一個是NotSure,另一個是Sure.
5.AbstractCells:表示一些cell的集合,主要有三個子類
???? BlockCells:表示一個由多個Cell組成的塊,例如一個2*2由4個Cell組成的塊,或者一個2*3由6個Cell組成的塊
???? HorizonCells:表示一個橫行,即:從(0,0)到(0,n)坐標確定的所有Cell的集合。
???? VerticalCells:表示一個縱行,即:從(0,0)到(n,0)坐標確定的所有Cell的集合。
6.AbstractPolicy:就是游戲的策略。
?? 這個主要表示的是:4*4的游戲,還是9*9的游戲。
?? 可以在以后對此類進行繼承和擴展,例如16*16的游戲我就沒有實現。
?? 主要擴展3個方法:
????????????????? 1)getValueRange,返回當前policy的value的個數。4*4的游戲的getValueRange返回的就應該是4。
??? ??? ? 2)getStep:表示當前policy中相鄰的兩個BlockCells的坐標差。
??? ??? ? 3)getIncrease:說不明白了:)(只可意會不可言傳。)
7.Game:進行Policy的場所(我一直想拋棄這個類)
8.TestGame:游戲運行的地方,包括從PolicyFactory取得指定的Policy,設置輸入輸出文件的路徑。
9.PolicyFactory:取得Policy的工廠。
??? getPolicy(int x) :這個方法獲得的是正方形的sudoku的策略。例如:4*4的,9*9,16*16。
??? getPolicy(int x, int y):這個方法獲得的是長方形的Sudoku的策略。例如:9*12的。
雖然是盡量避免bad code smell,但是由于能力有限,還是出現了一些不好的地方。
例如:之間的關聯關系還是很多,而且很強;抽象的方法和抽象類的個數偏少等等。
里面實現了三個解決sudoku的方法:
1.在一個Cell中出現的Value,不會在和這個Cell處在同一個AbstractCells中的所有Cell中出現;
2.如果一個Cell中,所有可能出現的Value的個數為1,那么Cell的Value必然是這個最后的Value;
2.如果一個Value,如果在當前AbstractCells的所有其他的Cell中都不可能出現,那么它必然是最后一個Cell的Value。
附件1:src code
http://www.tkk7.com/Files/GandofYan/sudoku.rar
附件2:輸入輸出文件的example
http://www.tkk7.com/Files/GandofYan/temp.rar
posted @
2006-07-13 16:19 混沌中立 閱讀(2163) |
評論 (4) |
編輯 收藏
如同Tom DeMacro說的:無法控制的東西就不能管理,無法測量的東西就無法控制。
軟件的度量對于設計者和開發者非常重要,之前只是對這些有一個簡單的了解。今天看來,了解的還遠遠不夠。
- Cyclomatic Complexity (圈復雜性)
- Response for Class (類的響應)
- Weighted methods per class (每個類重量方法)
一個系統中的所有類的這三個度量能夠說明這個系統的設計上的一些問題(不是全部),這三個度量越大越不好。
如果一個類這三個度量很高,證明了這個類需要重構了。
以第一個度量來說,有下面的一個表格:
CC Value | Risk |
1-10 | Low
risk program |
11-20 | Moderate
risk |
21-50 | High
risk |
>50 | Most
complex and highly unstable method |
CC數值高,可以通過減少if else(switch case也算)判斷來達到目的;
可以通過減少類與其他類的調用來減少RFC;
通過分割大方法和大類來達到減少WMPC.
而Uncle Bob和Jdepend的度量標準應該算是另一個度量系統。
用包中的每個類平均的內部關系數目作為包內聚性的一種表示方式。用于表示包和它的所有類之間的關系。
H=(R+1)/N
R:包內類的關系數目(與包外部的類沒有關系)
N:包內類的數量
被分析package的具體和抽象類(和接口)的數量,用于衡量package的可擴展性。
依賴于被分析package的其他package的數量,用于衡量pacakge的職責。
被分析package的類所依賴的其他package的數量,用于衡量package的獨立性。
被分析package中的抽象類和接口與所在package所有類數量的比例,取值范圍為0-1。
A=Cc/N
用于衡量package的不穩定性,取值范圍為0-1。I=0表示最穩定,I=1表示最不穩定。
I=Ce/(Ce+Ca)
??? ??? ? 被分析package和理想曲線A+I=1的垂直距離,用于衡量package在穩定性和抽象性之間的平衡。理想??? ??? ? 的package要么完全是抽象類和穩定(x=0,y=1),要么完全是具體類和不穩定(x=1,y=0)。
??? ??? ? 取值范圍為0-1,D=0表示完全符合理想標準,D=1表示package最大程度地偏離了理想標準。
??? ?? ?? D = |A+I-1|/0.70710678
??? ?? ?? 注:0.70710678*0.70710678 =2,既為“根號2“
我認為D是一個綜合的度量,架構和設計的改善可以通過D數值的減少來體現,反之就可以認為是設計和架構的退化。
讀過http://javaboutique.internet.com/tutorials/metrics/index.html之后的一些想法
另一篇中文的內容相近的文章可以參考http://www.jdon.com/artichect/coupling.htm
不過第二篇的中文文章中間關于Cyclomatic Complexity,有一個情況遺漏了
public void findApplications(String id, String name){
if(id!=null && name!=null) {
//do something
}else{
//do something
}
}
這種情況的CC不是2+1,而是2+1+1,依據是公式(1)。公式(2)應該是公式(1)的簡化版。
Cyclomatic Complexity
(CC) = no of decision points + no of logical operations +1 (1)
Cyclomatic Complexity (CC) = number of decision points +1 (2)
參考了JDepend的參數和Uncle Bob的《
Agile Software Development: Principles, Patterns, and Practices》
(敏捷軟件開發:原則、模式與實踐)
posted @
2006-06-07 10:52 混沌中立 閱讀(1512) |
評論 (3) |
編輯 收藏
轉自:http://www.keyusoft.cn/Contentview.aspx?year=2005&month=$10&day=$6&postid=123
通過一周左右的研究,對規則引擎有了一定的了解。現在寫點東西跟大家一起交流,本文主要針對RETE算法進行描述。我的文筆不太好,如果有什么沒講明白的或是說錯的地方,請給我留言。
首先申明,我的帖子借鑒了網上很流行的一篇帖子,好像是來自CSDN;還有一點,我不想做太多的名詞解釋,因為我也不是個研究很深的人,定義的不好怕被笑話。
好現在我們開始。
首先介紹一些網上對于規則引擎比較好的帖子。
1、 來自JAVA視頻網
2、? RETE算法的最原始的描述,我不知道在哪里找到的,想要的人可以留下E-mail
?
接著統一一下術語,很多資料里的術語都非常混亂。
1、? facts 事實,我們實現的時候,會有一個事實庫。用F表示。
2、? patterns 模板,事實的一個模型,所有事實庫中的事實都必須滿足模板中的一個。用P表示。
3、
? conditions
條件,規則的組成部分。也必須滿足模板庫中的一條模板。用C表示。我們可以這樣理解facts、patterns、conditions之間的關系。
Patterns是一個接口,conditions則是實現這個接口的類,而facts是這個類的實例。
4、? rules 規則,由一到多個條件構成。一般用and或or連接conditions。用R表示。
5、? actions 動作,激活一條rule執行的動作。我們這里不作討論。
6、? 還有一些術語,如:working-memory、production-memory,跟這里的概念大同小異。
7、? 還有一些,如:alpha-network、beta-network、join-node,我們下面會用到,先放一下,一會討論。
?
引用一下網上很流行的例子,我覺得沒講明白,我在用我的想法解釋一下。
?
假設在規則記憶中有下列三條規則
?
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算法會先將規則編譯成下列的樹狀架構排序網絡
而工作記憶內容及順序為{A(1),A(2),B(2),B(3),B(4),C(5)},當工作記憶依序進入網絡后,會依序儲存在符合條件的節點中,直到完全符合條件的推論規則推出推論。以上述例子而言, 最后推得D(2)。
?
讓我們來分析這個例子。
?
模板庫:(這個例子中只有一個模板,算法原描述中有不同的例子, 一般我們會用tuple,元組的形式來定義facts,patterns,condition)
P: (?A , ?x)? 其中的A可能代表一定的操作,如例子中的A,B,C,D,E ; x代表操作的參數。看看這個模板是不是已經可以描述所有的事實。
?
條件庫:(這里元組的第一項代表實際的操作,第二項代表形參)
C1: (A , <x>)
C2: (B , <x>)
C3: (C , <y>)
C4: (D , <x>)
C5: (E , <x>)
C6: (B , <y>)
?
事實庫:(第二項代表實參)
F1: (A,1)
F2: (A,2)
F3: (B,2)
F4: (B,3)
F5: (B,4)
F6: (C,5)
?
?????? 規則庫:
?????? ? R1: c1^c2^c3
?????? ? R2: c1^c2^c4
?????? ? R3: c1^c2^c5
?
??????
?????? 有人可能會質疑R1: c1^c2^c3,沒有描述出,原式中:
if A(x) and B(x) and C(y) then add D(x),A=B的關系。但請仔細看一下,這一點已經在條件庫中定義出來了。
?
?????? 下面我來描述一下,規則引擎中RETE算法的實現。
?????? 首先,我們要定一些規則,根據這些規則,我們的引擎可以編譯出一個樹狀結構,上面的那張圖中是一種簡易的表現,其實在實現的時候不是這個樣子的。
?????? 這就是beta-network出場的時候了,根據rules我們就可以確定beta-network,下面,我就畫出本例中的beta-network,為了描述方便,我把alpha-network也畫出來了。
??????
?

上圖中,左邊的部分就是beta-network,右邊是alpha-network,圓圈是join-node.
從上圖中,我們可以驗證,在beta-network中,表現出了rules的內容,其中r1,r2,r3共享了許多BM和join-node,這是由于這些規則中有共同的部分,這樣能加快match的速度。
右
邊的alpha-network是根據事實庫構建的,其中除alpha-network節點的節點都是根據每一條condition,從事實庫中
match過來的,這一過程是靜態的,即在編譯構建網絡的過程中已經建立的。只要事實庫是穩定的,即沒有大幅度的變化,RETE算法的執行效率應該是非常
高的,其原因就是已經通過靜態的編譯,構建了alpha-network。我們可以驗證一下,滿足c1的事實確實是w1,w2。
下
面我們就看一下,這個算法是怎么來運行的,即怎么來確定被激活的rules的。從top-node往下遍歷,到一個join-node,與AM for
c1的節點匯合,運行到match c1節點。此時,match c1節點的內容就是:w1,w2。繼續往下,與AM for
c2匯合(所有可能的組合應該是w1^w3,w1^w4,w1^w5,w2^w3,w2^w4,w2^w5),因為c1^c2要求參數相同,因此,
match
c1^c2的內容是:w2^w3。再繼續,這里有一個扇出(fan-out),其中只有一個join-node可以被激活,因為旁邊的AM只有一個非空。
因此,也只有R1被激活了。
解決扇出帶來的效率降低的問題,我們可以使用hashtable來解決這個問題。
RETE算法還有一些問題,如:facts庫變化,我們怎么才能高效的重建alpha-network,同理包括rules的變化對beta-network的影響。這一部分我還沒細看,到時候再貼出來吧。
posted @
2006-05-30 15:30 混沌中立 閱讀(1048) |
評論 (2) |
編輯 收藏
最近插件又加多了,eclipse老是死掉
?
一怒之下,刪除重裝
?
以前因為懶,沒有把插件的目錄和主體的目錄分開,這次也給它分開了
?
?
插件少了之后,eclipse確實快了不少
?
附eclipse啟動參數: -nl en_US vmargs -Xverify:none -Xms256M -Xmx1024M
-XX:PermSize=50M? -XX:+UseParallelGC
posted @
2006-05-30 13:48 混沌中立 閱讀(568) |
評論 (0) |
編輯 收藏
freemind一個比較不錯free的
mind map 軟件,很多人建議使用這個來管理自己的思路.
?
?
visio,就不介紹了.office里面有的東西.做流程圖來說,確實是比較好的軟件.但是在思路不清楚的時候,很難畫出什么有用的東西來.這點就比不上前面兩個東西了.不過對我來說visio可能更順手,因為我經常畫的是軟件流程圖........
posted @
2006-05-30 13:36 混沌中立 閱讀(1755) |
評論 (2) |
編輯 收藏
?
在最近的圍繞domain
object的討論中浮現出來了三種模型,(還有一些其他的旁枝,不一一分析了),經過一番討論,各種問題逐漸清晰起來,在這里我試圖做一個總結,便于大家了解和掌握。
第一種模型:只有getter/setter方法的純數據類,所有的業務邏輯完全由business
object來完成(又稱TransactionScript),這種模型下的domain object被Martin Fowler稱之為“貧血的domain
object”。下面用舉一個具體的代碼來說明,代碼來自Hibernate的caveatemptor,但經過我的改寫:
一個實體類叫做Item,指的是一個拍賣項目
一個DAO接口類叫做ItemDao
一個DAO接口實現類叫做ItemDaoHibernateImpl
一個業務邏輯類叫做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方法省略不寫,避免篇幅太長 }
|
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完成具體的持久化工作,請注意,數據庫資源的獲取和釋放是在ItemDaoHibernateImpl
里面處理的,每個DAO方法調用之前打開Session,DAO方法調用之后,關閉Session。(Session放在ThreadLocal中,保證一次調用只打開關閉一次)
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);?
? ?//? 調用DAO完成持久化操作 ? ? return newBid; ? ?
} }
|
事務的管理是在ItemManger這一層完成的,ItemManager實現具體的業務邏輯。除了常見的和CRUD有關的簡單邏輯之外,這里還有一個placeBid的邏輯,即項目的競標。
以上是一個完整的第一種模型的示例代碼。在這個示例中,placeBid,loadItemById,findAll等等業務邏輯統統放在ItemManager中實現,而Item只有getter/setter方法。
?
?
第二種模型,也就是Martin Fowler指的rich domain object是下面這樣子的:
一個帶有業務邏輯的實體類,即domain object是Item
一個DAO接口ItemDao
一個DAO實現ItemDaoHibernateImpl
一個業務邏輯對象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);? //
請注意這一句,透明的進行了持久化,但是不能在這里調用ItemDao,Item不能對ItemDao產生依賴! ? ? ? ? ? ?
? ? return newBid;
? ? } }
|
競標這個業務邏輯被放入到Item中來。請注意this.getBids.add(newBid);
如果沒有Hibernate或者JDO這種O/R
Mapping的支持,我們是無法實現這種透明的持久化行為的。但是請注意,Item里面不能去調用ItemDAO,對ItemDAO產生依賴!
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);?
? // 必須顯式的調用DAO,保持持久化 ? ? } }
|
在第二種模型中,placeBid業務邏輯是放在Item中實現的,而loadItemById和findAll業務邏輯是放在
ItemManager中實現的。不過值得注意的是,即使placeBid業務邏輯放在Item中,你仍然需要在ItemManager中簡單的封裝一層,以保證對placeBid業務邏輯進行事務的管理和持久化的觸發。
這種模型是Martin Fowler所指的真正的domain
model。在這種模型中,有三個業務邏輯方法:placeBid,loadItemById和findAll,現在的問題是哪個邏輯應該放在Item
中,哪個邏輯應該放在ItemManager中。在我們這個例子中,placeBid放在Item中(但是ItemManager也需要對它進行簡單的封裝),loadItemById和findAll是放在ItemManager中的。
切分的原則是什么呢? Rod Johnson提出原則是“case by case”,可重用度高的,和domain
object狀態密切關聯的放在Item中,可重用度低的,和domain object狀態沒有密切關聯的放在ItemManager中。
我提出的原則是:看業務方法是否顯式的依賴持久化。
Item的placeBid這個業務邏輯方法沒有顯式的對持久化ItemDao接口產生依賴,所以要放在Item中。請注意,如果脫離了Hibernate這個持久化框架,Item這個domain
object是可以進行單元測試的,他不依賴于Hibernate的持久化機制。它是一個獨立的,可移植的,完整的,自包含的域對象。
而loadItemById和findAll這兩個業務邏輯方法是必須顯式的對持久化ItemDao接口產生依賴,否則這個業務邏輯就無法完成。如果你要把這兩個方法放在Item中,那么Item就無法脫離Hibernate框架,無法在Hibernate框架之外獨立存在。
?
?
第三種模型印象中好像是firebody或者是Archie提出的(也有可能不是,記不清楚了),簡單的來說,這種模型就是把第二種模型的domain
object和business object合二為一了。所以ItemManager就不需要了,在這種模型下面,只有三個類,他們分別是:
Item:包含了實體類信息,也包含了所有的業務邏輯
ItemDao:持久化DAO接口類
ItemDaoHibernateImpl:DAO接口的實現類
由于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);?
? ? //? 調用DAO進行顯式持久化 ? ? ? ?
return newBid;
? ? } }
|
在這種模型中,所有的業務邏輯全部都在Item中,事務管理也在Item中實現。
?
?
在上面三種模型之外,還有很多這三種模型的變種,例如partech的模型就是把第二種模型中的DAO和
Manager三個類合并為一個類后形成的模型;例如frain....(id很長記不住)的模型就是把第三種模型的三個類完全合并為一個單類后形成的模型;例如Archie是把第三種模型的Item又分出來一些純數據類(可能是,不確定)形成的一個模型。
但是不管怎么變,基本模型歸納起來就是上面的三種模型,下面分別簡單評價一下:
第一種模型絕大多數人都反對,因此反對理由我也不多講了。但遺憾的是,我觀察到的實際情形是,很多使用Hibernate的公司最后都是這種模型,這里面有很大的原因是很多公司的技術水平沒有達到這種層次,所以導致了這種貧血模型的出現。從這一點來說,Martin
Fowler的批評聲音不是太響了,而是太弱了,還需要再繼續吶喊。
第二種模型就是Martin
Fowler一直主張的模型,實際上也是我一直在實際項目中采用這種模型。我沒有看過Martin的POEAA,之所以能夠自己摸索到這種模型,也是因為從02年我已經開始思考這個問題并且尋求解決方案了,但是當時沒有看到Hibernate,那時候做的一個小型項目我已經按照這種模型來做了,但是由于沒有O/R
Mapping的支持,寫到后來又不得不全部改成貧血的domain
object,項目做完以后再繼續找,隨后就發現了Hibernate。當然,現在很多人一開始就是用Hibernate做項目,沒有經歷過我經歷的那個階段。
不過我覺得這種模型仍然不夠完美,因為你還是需要一個業務邏輯層來封裝所有的domain
logic,這顯得非常羅嗦,并且業務邏輯對象的接口也不夠穩定。如果不考慮業務邏輯對象的重用性的話(業務邏輯對象的可重用性也不可能好),很多人干脆就去掉了xxxManager這一層,在Web層的Action代碼直接調用xxxDao,同時容器事務管理配置到Action這一層上來。
Hibernate的caveatemptor就是這樣架構的一個典型應用。
第三種模型是我很反對的一種模型,這種模型下面,Domain
Object和DAO形成了雙向依賴關系,無法脫離框架測試,并且業務邏輯層的服務也和持久層對象的狀態耦合到了一起,會造成程序的高度的復雜性,很差的靈活性和糟糕的可維護性。也許將來技術進步導致的O/R
Mapping管理下的domain object發展到足夠的動態持久透明化的話,這種模型才會成為一個理想的選擇。就像O/R
Mapping的流行使得第二種模型成為了可能(O/R Mapping流行以前,我們只能用第一種模型,第二種模型那時候是不現實的)。
?
?
既然大家都統一了觀點,那么就有了一個很好的討論問題的基礎了。Martin Fowler的Domain
Model,或者說我們的第二種模型難道是完美無缺的嗎?當然不是,接下來我就要分析一下它的不足,以及可能的解決辦法,而這些都來源于我個人的實踐探索。
在第二種模型中,我們可以清楚的把這4個類分為三層:
1、實體類層,即Item,帶有domain logic的domain
object
2、DAO層,即ItemDao和ItemDaoHibernateImpl,抽象持久化操作的接口和實現類
3、業務邏輯層,即ItemManager,接受容器事務控制,向Web層提供統一的服務調用
在這三層中我們大家可以看到,domain
object和DAO都是非常穩定的層,其實原因也很簡單,因為domain object是映射數據庫字段的,數據庫字段不會頻繁變動,所以domain
object也相對穩定,而面向數據庫持久化編程的DAO層也不過就是CRUD而已,不會有更多的花樣,所以也很穩定。
問題就在于這個充當business workflow facade的業務邏輯對象,它的變動是相當頻繁的。業務邏輯對象通常都是無狀態的、受事務控制的、Singleton類,我們可以考察一下業務邏輯對象都有哪幾類業務邏輯方法:
第一類:DAO接口方法的代理,就是上面例子中的loadItemById方法和findAll方法。
ItemManager之所以要代理這種類,目的有兩個:向Web層提供統一的服務調用入口點和給持久化方法增加事務控制功能。這兩點都很容易理解,你不能既給Web層程序員提供xxxManager,也給他提供xxxDao,所以你需要用xxxManager封裝xxxDao,在這里,充當了一個簡單代理功能;而事務控制也是持久化方法必須的,事務可能需要跨越多個DAO方法調用,所以必須放在業務邏輯層,而不能放在DAO層。
但是必須看到,對于一個典型的web應用來說,絕大多數的業務邏輯都是簡單的CRUD邏輯,所以這種情況下,針對每個DAO方法,xxxManager都需要提供一個對應的封裝方法,這不但是非常枯燥的,也是令人感覺非常不好的。
第二類:domain
logic的方法代理。就是上面例子中placeBid方法。雖然Item已經有了placeBid方法,但是ItemManager仍然需要封裝一下Item的placeBid,然后再提供一個簡單封裝之后的代理方法。
這和第一種情況類似,其原因也一樣,也是為了給Web層提供一個統一的服務調用入口點和給隱式的持久化動作提供事務控制。
同樣,和第一種情況一樣,針對每個domain logic方法,xxxManager都需要提供一個對應的封裝方法,同樣是枯燥的,令人不爽的。
第三類:需要多個domain object和DAO參與協作的business
workflow。這種情況是業務邏輯對象真正應該完成的職責。
在這個簡單的例子中,沒有涉及到這種情況,不過大家都可以想像的出來這種應用場景,因此不必舉例說明了。
通過上面的分析可以看出,只有第三類業務邏輯方法才是業務邏輯對象真正應該承擔的職責,而前兩類業務邏輯方法都是“無奈之舉”,不得不為之的事情,不但枯燥,而且令人沮喪。
分析完了業務邏輯對象,我們再回頭看一下domain object,我們要仔細考察一下domain
logic的話,會發現domain logic也分為兩類:
第一類:需要持久層框架隱式的實現透明持久化的domain
logic,例如Item的placeBid方法中的這一句:
java代碼:?
|
this.getBids().add(newBid);
|
上面已經著重提到,雖然這僅僅只是一個Java集合的添加新元素的操作,但是實際上通過事務的控制,會潛在的觸發兩條SQL:一條是insert一條記錄到bid表,一條是更新item表相應的記錄。如果我們讓Item脫離Hibernate進行單元測試,它就是一個單純的Java集合操作,如果我們把他加入到Hibernate框架中,他就會潛在的觸發兩條SQL,這就是隱式的依賴于持久化的domain logic。
特別請注意的一點是:在沒有Hibernate/JDO這類可以實現“透明的持久化”工具出現之前,這類domain logic是無法實現的。
對于這一類domain logic,業務邏輯對象必須提供相應的封裝方法,以實現事務控制。
第二類:完全不依賴持久化的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()); ? ? } }
|
注意這個isAllowReply方法,他和持久化完全不發生一丁點關系。在實際的開發中,我們同樣會遇到很多這種不需要持久化的業務邏輯(主要發生在日期運算、數值運算和枚舉運算方面),這種domain
logic不管脫離不脫離所在的框架,它的行為都是一致的。對于這種domain
logic,業務邏輯層并不需要提供封裝方法,它可以適用于任何場合。
posted @
2006-05-30 13:31 混沌中立 閱讀(3058) |
評論 (1) |
編輯 收藏
原文地址:
http://www.cnblogs.com/idior/archive/2005/07/04/186086.html近日?有關o/r?m的討論突然多了起來.?在這里覺得有必要澄清一些概念,?免的大家討論來討論去,?才發現最根本的理解有問題.
1.?何謂實體?
實體(類似于j2ee中的Entity?Bean)通常指一個承載數據的對象,?但是注意它也是可以有行為的!?只不過它的行為一般只操作自身的數據.?比如下面這個例子:
class?Person
{
??string?firstName;
??string?lastName;
??public?void?GetName()
??{
?????return??lastName+firstName;
??}???
}
GetName就是它的一個行為.
2?何謂對象?
對象最重要的特性在于它擁有行為.?僅僅擁有數據,你可以稱它為對象,?但是它卻失去它最重要的靈魂.?
class?Person
{
??string?firstName;
??string?lastName;
??Role?role;
??int?baseWage;
??public?void?GetSalary()
??{
?????return?baseWage*role.GetFactory();
??}???
}
這樣需要和別的對象(不是Value?Object)打交道的對象,我就不再稱其為實體.?領域模型就是指由這些具有業務邏輯的對象構成的模型.
3.?E/R?M?or?O/R?M?!!
仔細想想我們為什么需要o/r?m,無非是想利用oo的多態來處理復雜的業務邏輯,?而不是靠一堆的if?else.?
而現在在很多人的手上o/r?m全變成了e/r?m.他們不考慮對象的行為,?而全關注于如何保存數據.這樣也難怪他們會產生將CRUD這些操作放入對象中的念頭.?如果你不能深刻理解oo,?那么我不推薦你使用o/r?m,?Table?Gateway,?Row?Gateway才是你想要的東西.
作為一個O/R?M框架,很重要的一點就是實現映射的透明性(Transparent),比較顯著的特點就是在代碼中我們是看不到SQL語句的(框架自動生成了)。這里所指的O/R?M就是類似于此的框架,
4.?POEAA中的相關概念
??很多次發現有人錯用其中的概念,?這里順便總結一下:
??1.?Table?Gateway
????以表為單位的實體,基本沒有行為,只有CRUD操作.
??2.?Row?Gateway
????以行為單位的實體,基本沒有行為,只有CRUD操作.
??3.?Active?Record
????以行為單位的實體,擁有一些基本的操作自身數據的行為(如上例中的GetName),同時包含有CRUD操作.
其實Active?Record最符合某些簡單的需求,?接近于E/R?m.
通常也有很多人把它當作O/R?m.不過需要注意的是Active?Record中是充滿了SQL語句的(不像orm的SQL透明),?所以有人想起來利用O/R?m來實現"Active?Record",?雖然在他們眼里看起來很方便,?其實根本就是返祖.
用CodeGenerator來實現Active?Record也許是一個比較好的方法.
??4.?Data?Mapper
這才是真正的O/R?m,Hibernate等等工具的目標.
5.O/R?M需要關注的地方?(希望大家幫忙完善一下)
?1.?關聯,?O/R?M是如何管理類之間的關聯.當然這不僅于o/r?m有關與設計者的設計水平也有很大關系.
?2.?O/R?M對繼承關系的處理.
?3.?O/R?M對事務的支持.
?4.?O/R?M對查詢的支持.
?
以上觀點僅屬個人意見,?不過在大家討論有關O/R?m之前,?希望先就一些基本概念達成共識,?不然討論下去會越離越遠.?
(建議:?如果對oo以及dp沒有一定程度的了解,?最好別使用o/r?m,?dataset?加上codesmith或許是更好的選擇)
posted @
2006-05-30 13:20 混沌中立 閱讀(380) |
評論 (0) |
編輯 收藏
from http://www.code365.com/web/122/Article/17927.Asp
Thomas Bayes,一位偉大的數學大師,他的理論照亮了今天的計算領域,和他的同事們不同:他認為上帝的存在可以通過方程式證明,他最重要的作品被別人發行,而他已經去世241年了。
18世紀牧師們關于概率的理論成為應用發展的數學基礎的一部分。
搜索巨人Google和Autonomy,一家出售信息恢復工具的公司,都使用了貝葉斯定理(Bayesian
principles)為數據搜索提供近似的(但是技術上不確切)結果。研究人員還使用貝葉斯模型來判斷癥狀和疾病之間的相互關系,創建個人機器人,開發
能夠根據數據和經驗來決定行動的人工智能設備。
雖然聽起來很深奧,而這個原理的意思--大致說起來--卻很簡單:某件事情發生的概率大致可以由它過去發生的頻率近似地估計出來。研究人員把這個原理應用在每件事上,從基因研究到過濾電子郵件。
在明尼蘇達州大學的網站上能夠找到一份詳細的數學概要。而在Gametheory.net上的一個Bayes Rule Applet程序讓你能夠回答諸如“如果你測試某種疾病,有多大風險”之類的問題。
貝葉斯理論的一個出名的倡導者就是微軟。該公司把概率用于它的Notification Platform。該技術將會被內置到微軟未來的軟件中,而且讓計算機和蜂窩電話能夠自動地過濾信息,不需要用戶幫助,自動計劃會議并且和其他人聯系。
如果成功的話,該技術將會導致“context server”--一種電子管家的出現,它能夠解釋人的日常生活習慣并在不斷變換的環境中組織他們的生活。
“Bayes的研究被用于決定我應該怎樣最好地分配計算和帶寬,” Eric Horvitz表示,他是微軟研究部門Adaptive
Systems & Interaction
Group的高級研究員和分組管理者。“我個人相信在這個不確定的世界里,你不能夠知道每件事,而概率論是任何智能的基礎。”
到今年年底,Intel也將發布它自己的基于貝葉斯理論的工具包。一個關于照相機的實驗警告醫生說病人可能很快遭受痛苦。在本周晚些時候在該公司的Developer Forum(開發者論壇)上將討論這種發展。
雖然它在今天很流行,Bayes的理論并不是一直被廣泛接受的:就在10年前,Bayes研究人員還在他們的專業上躊躇不前。但是其后,改進的數學模型,更快的計算機和實驗的有效結果增加了這種學派新的可信程度。
“問題之一是它被過度宣傳了,” Intel微處理器實驗室的應用軟件和技術管理經理Omid Moghadam表示。“事實上,能夠處理任何事情的能力并不存在。真正的執行在過去的10年里就發生了。”
Bayes啞元
Bayes的理論可以粗略地被簡述成一條原則:為了預見未來,必須要看看過去。Bayes的理論表示未來某件事情發生的概率可以通過計算它過去發生的頻率來估計。一個彈起的硬幣正面朝上的概率是多少?實驗數據表明這個值是50%。
“Bayes表示從本質上說,每件事都有不確定性,你有不同的概率類型,”斯坦佛的管理科學和工程系(Department of Management Science and Engineering at Stanford)的教授Ron Howard表示。
例如,假設不是硬幣,一名研究人員把塑料圖釘往上拋,想要看看它釘頭朝上落地的概率有多大,或者有多少可能性是側面著地,而釘子是指向什么方向的。形狀,成型過程中的誤差,重量分布和其他的因素都會影響該結果。
Bayes技術的吸引力在于它的簡單性。預測完全取決于收集到的數據--獲得的數據越多,結果就越好。另一個優點在于Bayes模型能夠自我糾正,也就是說數據變化了,結果也就跟著變化。
概率論的思想改變了人們和計算機互動的方式。“這種想法是計算機能夠更象一個幫助者而不僅僅是一個終端設備,” Peter Norvig表示。他是Google的安全質量總監。他說“你在尋找的是一些指導,而不是一個標準答案。”
從這種轉變中,研究獲益非淺。幾年前,所謂的Boolean搜索引擎的一般使用需要把搜索按照“if, and, or but”的語法進行提交,然后去尋找匹配的詞。現在的搜索引擎采用了復雜的運算法則來搜索數據庫,并找出可能的匹配。
如同圖釘的那個例子顯示的那樣,復雜性和對于更多數據的需要可能很快增長。由于功能強大的計算機的出現,對于把好的猜測轉變成近似的輸出所必須的結果進行控制成為可能。
更重要的是,UCLA的Judea Pearl這樣的研究人員研究出如何讓Bayes模型能夠更好地追蹤不同的現象之間條件關系的方法,這樣能夠極大地減少計算量。
例如,對于人口進行大規模的關于肺癌成因的調查可能會發現它是一種不太廣泛的疾病,但是如果局限在吸煙者范圍內進行調查就可能會發現一些關聯性。對于肺癌患者進行檢查能夠幫助調查清楚習慣和這種疾病之間的關系。
“每一個單獨的屬性或者征兆都可能取決于很多不同的事情,但是直接決定它的卻是為數不多的事情,”斯坦佛計算機科學系(computer
science department at Stanford)的助理教授Daphne
Koller表示。“在過去的15年左右的時間里,人們在工具方面進行了改革,這讓你能夠描繪出大量人群的情況。”
和其他一些項目一樣,Koller是使用概率論技術來更好地把病癥和疾病聯系起來,并把遺傳基因和特定的細胞現象聯系起來。
記錄演講
一項相關的技術,名為Hidden Markov模型,讓概率能夠預測次序。例如,一個演講識別應用知道經常在“q”之后的字母是“u”。除了這些,該軟件還能夠計算“Qagga”(一種滅絕了的斑馬的名稱)一詞出現的概率。
概率技術已經內置在微軟的產品中了。Outlook Mobile
Manage是一個能夠決定什么時候往移動設備上發出一封內勤的電子郵的軟件。它是從Priorities發展而來的,Priorities是微軟在
1998年公布的一個實驗系統。Windows XP的故障檢修引擎也依賴于概率計算。
隨著該公司的Notification Platform開始內置在產品中,在未來的一年中會有更多的應用軟件發布,微軟的Horvitz這樣表示。
Notification
Platform的一個重要組成部分名為Coordinate,它從個人日歷,鍵盤,傳感器照相機以及其他來源收集數據,來了解某個人生活和習慣。收集的
數據可能包括到達的時間,工作時間和午餐的時間長度,哪種類型的電話或電子郵件被保存,而哪些信息被刪除,在某天的特定時間里鍵盤被使用的頻率,等等。
這些數據可以被用來管理信息流和使用者收到的其他信息。例如,如果一位經理在下午2:40發送了一封電子郵件給一名員工,
Coordinate可以檢查該員工的日歷程序,然后發現他在下午2:00有一個會議。該程序還可以掃描關于該員工習慣的數據,然后發現該員工通常會在有
會議之后大約一個小時才重新使用鍵盤。該程序可能還能夠發現該名員工通常會在5分鐘之內回復該經理的電子郵件。根據上面這些數據,該軟件能夠估計出該員工
可能至少在20分鐘之內不可能回復該電子郵件,該軟件可能會把這條信息發送到該員工的手提電話上。同時,該軟件可能會決定不把別人的電子郵件也轉發出去。
“我們正在平衡以打攪你為代價所獲得信息的價值,” Horvitz表示。使用這個軟件,他堅持道,“能夠讓更多的人跟上事情的發展,而不被大量的信息所淹沒。”
Horvitz補充道,隱私和對于這些功能的用戶控制是確定的。呼叫者并不知道為什么一條信息可能會被優先或推遲處理。
微軟還把Bayes模型使用在其他的一些產品上,包括DeepListener 以及Quartet (語音激活),SmartOOF 以及TimeWave (聯系控制)。消費者多媒體軟件也獲益非淺,Horvitz表示。
Bayes技術不僅僅被應用在PC領域。在University of
Rochester,研究人員發現一個人的步伐可以在一步前發生改變。雖然這種改變對于人類來說太過于細微,一臺和電腦連接在一起的照相機可以捕捉并跟蹤
這種動作。如果行走異常出現,計算機就能夠發出警報。
一個實驗用的安全照相機采用了同樣的原理:大部分到達機場的人都會在停車以后直接走向目的地,所以如果有人停了車,然后走向另一輛車就不太正常,因此就可能引發警報。今年秋天一個創建Bayes模型和技術信息的基本引擎將會公布在Intel的開發者網站上。
理論沖突
雖然該技術聽起來簡單易懂,關于它的計算可能卻比較慢。Horvitz回憶說他是斯坦佛20世紀80年代僅有的兩個概率和人工智能的畢業生之一。其他所有的人學習的是邏輯系統,采用的是“if and then”的模式和世界互動。
“概率論那時候不流行,” Horvitz表示。但是當邏輯系統不能夠預測所有的意外情況時,潮流發生了轉變。
很多研究人員開始承認人類的決策過程比原來想象的要神秘的多。“在人工智能領域存在著文化偏見,” Koller表示。“人們現在承認他們并不知道他們的腦子是如何工作的。”
即便在他的時代,Bayes發現他自己置身于主流之外。他于1702年出生于倫敦,后來他成為了一名Presbyterian
minister。雖然他看到了自己的兩篇論文被發表了,他的理論很有效,但是《Essay Toward Solving a Problem in
the Doctrine of Chances》卻一直到他死后的第三年,也就是1764年才被發表。
他的王室成員身份一直是個謎,直到最近幾年,新發現的一些信件表明他私下和英格蘭其他一些思想家看法一致。
“就我所知,他從來沒有寫下貝葉斯定理,” Howard表示。
神學家Richard Price和法國的數學家Pierre Simon
LaPlace成為了早期的支持者。該理論和后來George Boole,布爾數學之父,的理論背道而馳。George
Boole的理論是基于代數邏輯的,并最終導致了二進制系統的誕生。也是皇室成員之一的Boole死于1864年。
雖然概率的重要性不容置疑,可是關于它的應用的爭論卻沒有停止過。批評者周期性地聲稱Bayes模型依賴于主觀的數據,而讓人類去判斷答案是否正確。而概率論模型沒有完全解決在人類思維過程中存在的細微差別的問題。
“兒童如何學習現在還不是很清楚,”IBM研究部門的科學和軟件副總裁 Alfred
Spector這樣表示。他計劃把統計學方法和邏輯系統在他的Combination
Hypothesis之中結合起來。“我最初相信是統計學的范疇,但是從某方面說,你將會發現不僅僅是統計學的問題。”
但是,很有可能概率論是基礎。
“這是個基礎,” Horvitz表示。“它被忽略了一段時間,但是它是推理的基礎。”
posted @
2006-05-30 12:51 混沌中立 閱讀(435) |
評論 (0) |
編輯 收藏
最近在看<
敏捷軟件開發:原則、模式與實踐
>在網上找了一些資料,就把他們集合到了一起,便于自己學習
此篇內容來自一下兩處
http://blog.joycode.com/microhelper/archive/2004/11/30/40013.aspx
http://www.tkk7.com/ghawk/
另:關于面向對象設計的原則比較權威的是Uncle Bob--
Robert C. Martin的
http://c2.com/cgi/wiki?PrinciplesOfObjectOrientedDesign
單一職責原則——SRP
就一個類而言,應該僅有一個引起它的變化的原因?
原則
最簡單,最單純的事情最容易控制,最有效
類的職責簡單而且集中,避免相同的職責分散到不同的類之中,避免一個類承擔過多的職責
減少類之間的耦合
當需求變化時,只修改一個地方
組件
每個組件集中做好一件事情
組件的顆粒度
發布的成本
可重用的成本
?
方法
避免寫臃腫的方法
Extract Method
?
重構
Move Field/Move Class
Extract Method/Extract Class
?
最簡單的,也是最難以掌握的原則
?
實例分析
單一職責很容易理解,也很容易實現。所謂單一職責,就是一個設計元素只做一件事。什么是“只做一件事”?簡單說就是少管閑事。現實中就是如此,如果要你專心做一件事情,任何人都有信心可以做得很出色。但如果,你整天被亂七八糟的事所累,還有心思和精力把每件事都作好么?

?????
“單一職責”就是要在設計中為每種職責設計一個類,彼此保持正交,互不干涉。這個雕塑(二重奏)就是正交的一個例子,鋼琴家和小提琴家各自演奏自己的樂
譜,而結果就是一個和諧的交響樂。當然,真實世界中,演奏小提琴和彈鋼琴的必須是兩個人,但是在軟件中,我們往往會把兩者甚至更多攪和到一起,很多時候只
是為了方便或是最初設計的時候沒有想到。?
??????這樣的例子在設計中很常見,書中就給了一個很好的例子:調制解調器。這是一個調制
解調器最基本的功能。但是這個類事實上完成了兩個職責:連接的建立和中斷、數據的發送和接收。顯然,這違反了SRP。這樣做會有潛在的問題:當僅需要改變
數據連接方式時,必須修改Modem類,而修改Modem類的結果就是使得任何依賴Modem類的元素都需要重新編譯,不管它是不是用到了數據連接功能。
解決的辦法,書中也已經給出:重構Modem類,從中抽出兩個接口,一個專門負責連接、另一個專門負責數據發送。依賴Modem類的元素也要做相應的細
化,根據職責的不同分別依賴不同的接口。最后由ModemImplementation類實現這兩個接口。

??????從這個例子中,我們不難發現,違反SRP通常是由于過于“真實”地設計了一個類所造成的。因此,解決辦法是往更高一層進行抽象
化提取,將對某個具體類的依賴改變為對一組接口或抽象類的依賴。當然,這個抽象化的提取應該根據需要設計,而不是盲目提取。比如剛才這個Modem的例子
中,如果有必要,還可以把DataChannel抽象為DataSender和DataReceiver兩個接口。
開放封閉原則——OCP
軟件實體(類,模塊,函數)應該是可以擴展的,但是不可修改的?
原則
?
對擴展是開放的,當需求改變時我們可以對模塊進行擴展,使其具有新的功能
對更改是封閉的,對模塊擴展時,不需要改動原來的代碼
面對抽象而不是面對細節,抽象比細節活的更長
僵化的設計——如果程序中一處改動產生連鎖反應。
方法
條件case?? if/else 語句
?
重構
Replace Type Code With Class
Replace Type Code With State/Strategy
Replace Conditional with polymorphism
?
實例
開閉原則很簡單,一句話:“Closed for Modification; Open for Extension”——“對變更關閉;對擴展開放”。開閉原則其實沒什么好講的,我將其歸結為一個高層次的設計總則。就這一點來講,OCP的地位應該比SRP優先。
OCP的動機很簡單:軟件是變化的。不論是優質的設計還是低劣的設計都無法回避這一問題。OCP說明了軟件設計應該盡可能地使架構穩定而又容易滿足不同的需求。
為什么要OCP?答案也很簡單——重用。
“重用”,并不是什么軟件工程的專業詞匯,它是工程界所共用的詞匯。早在軟件出現前,工程師們就在實踐“重用”了。比如機械產品,通過零部
件的組裝得到最終的能夠使用的工具。由于機械部件的設計和制造過程是極其復雜的,所以互換性是一個重要的特性。一輛車可以用不同的發動機、不同的變速箱、
不同的輪胎……很多東西我們直接買來裝上就可以了。這也是一個OCP的例子。(可能是由于我是搞機械出身的吧,所以就舉些機械方面的例子^_^)。
如何在OO中引入OCP原則?把對實體的依賴改為對抽象的依賴就行了。下面的例子說明了這個過程:
05賽季的時候,一輛F1賽車有一臺V10引擎。但是到了06賽季,國際汽聯修改了規則,一輛F1賽車只能安裝一臺V8引擎。車隊很快投入了新賽車
的研發,不幸的是,從工程師那里得到消息,舊車身的設計不能夠裝進新研發的引擎。我們不得不為新的引擎重新打造車身,于是一輛新的賽車誕生了。但是,麻煩
的事接踵而來,國際汽聯頻頻修改規則,搞得設計師在“賽車”上改了又改,最終變得不成樣子,只能把它廢棄。

為了能夠重用這輛昂貴的賽車,工程師們提出了解決方案:首先,在車身的設計上預留出安裝引擎的位置和管線。然后,根據這些設計好的規范設計引擎(或是引擎的適配器)。于是,新的賽車設計方案就這樣誕生了。
?
顯然,通過重構,這里應用的是一個典型的Bridge模式。這個實現的關鍵之處在于我們預先給引擎留出了位置!我們不必因為對引擎的規則的頻頻變更而制造相當多的車身,而是盡可能地沿用和改良現有的車身。
說到這里,想說一說OO設計的一個誤區。
學
習OO語言的時候,為了能夠說明“繼承”(或者說“is-a”)這個概念,教科書上經常用實際生活中的例子來解釋。比如汽車是車,電車是車,F1賽車是汽
車,所以車是汽車、電車、F1賽車的上層抽象。這個例子并沒有錯。問題是,這樣的例子過于“形象”了!如果OO設計直接就可以將現實生活中的概念引用過
來,那也就不需要什么軟件工程師了!OO設計的關鍵概念是抽象。如果沒有抽象,那所有的軟件工程師的努力都是徒勞的。因為如果沒有抽象,我們只能去構造世
界中每一個對象。上面這個例子中,我們應該看到“引擎”這個抽象的存在,因為車隊的工程師們為它預留了位置,為它制定了設計規范。
上面這個設計也
實現了后面要說的DIP(依賴倒置原則)。但是請記住,OCP是OO設計原則中高層次的原則,其余的原則對OCP提供了不同程度的支持。為了實現OCP,
我們會自覺或者不自覺地用到其它原則或是諸如Bridge、Decorator等設計模式。然而,對于一個應用系統而言,實現OCP并不是設計目的,我們
所希望的只是一個穩定的架構。所以對OCP的追求也應該適可而止,不要陷入過渡設計。正如Martin本人所說:“No significant
program can be 100% closed.”“Closure not complete but strategic”
Liskov替換原則—— LSP?
子類型必須能夠替換它的基類型
原則
主要針對繼承的設計原則
所有派生類的行為功能必須和客戶程序對其基類所期望的保持一致。
派生類必須滿足基類和客戶程序的約定
IS-A是關于行為方式的,依賴客戶程序的調用方式
?
重構
Extract Supper Class
?
實例
依賴倒置原則—— DIP?
a:高層模塊不應依賴于底層模塊,兩者都應該依賴于抽象
b:抽象不應該依賴于細節,細節應該依賴于抽象
?
原則
如何解釋倒置
高層依賴底層,重用變得困難,而最經常重用的就是framework和各個獨立的功能組件
高層依賴底層,底層的改動直接反饋到高層,形成依賴的傳遞
面向接口的編程
?
實例
Ioc模式
DomainObject / DomianObjectDataService
?
接口隔離原則—— ISP?
使用多個專門的接口比使用單一的總接口總要好。換而言之,從一個客戶類的角度來講:一個類對另外一個類的依賴性應當是建立在最小接口上的。
原則過于臃腫的接口是對接口的污染。不應該強迫客戶依賴于它們不用的方法。
My object-oriented umbrella(摘自Design Patterns Explained)
Let me tell you about my great umbrella. It is large enough to get
into! In fact, three or four other people can get in it with me. While
we are in it, staying out of the rain, I can move it from one place to
another. It has a stereo system to keep me entertained while I stay
dry. Amazingly enough, it can also condition the air to make it warmer
or colder. It is one cool umbrella.
My umbrella is convenient. It sits there waiting for me. It has
wheels on it so that I do not have to carry it around. I don't even
have to push it because it can propel itself. Sometimes, I will open
the top of my umbrella to let in the sun. (Why I am using my umbrella
when it is sunny outside is beyond me!)
In Seattle, there are hundreds of thousands of these umbrellas in all kinds of colors. Most people call them cars.
實現方法:
1、?使用委托分離接口
2、?使用多重繼承分離接口
想到一個朋友說的話:所有的接口都只有一個方法,具體的類根據自己需要什么方法去實現接口
posted @
2006-04-09 14:06 混沌中立 閱讀(589) |
評論 (0) |
編輯 收藏
工作了幾年,在不同的公司進入了幾個不同的團隊.感覺團隊之間的差異很大.
1.一個好的團隊,是需要時間來培養的.團隊中的成員需要時間來互相熟悉,這個熟悉不單是平常說的認識,還要包括熟悉其他的編程方式設計傾向,工作習慣.只有這樣以后,在討論問題討論方案的時候,可以形成默契,基本簡單幾句就會明白在說什么問題.團隊也需要時間來形成團隊的風格,一個有團隊所有成員的風格組合在一起形成的風格.包括文檔,編碼,設計,溝通等方面上的一致風格.中間需要不斷進行review,按照review的結果,對所有成果物進行修改.
2.一個好的團隊的人員流動應該是良性的.這個良性流動指的是有人員的變化,但是變化的數量和范圍不會使得團隊的風格發生大的變化,如果一個10人的團隊,突然發生的5人的變化,就是說調整到其他團隊5個人,又調整進來5個人,那對于這個團隊基本可以算是重新形成一個新的團隊了.
3.一個好的團隊,不單需要團隊內部成員的努力,同時也需要SPEG和QA在團隊之外對團隊的開發流程的監督和規范.如果沒有了解開發流程和開發規范的SPEG對流程進行監督,即使團隊形成了風格,那這個風格很有可能不是健康的風格,可能會導致團隊在以后的開發的過程中產生問題.
4.一個好的團隊,需要比較有控制力的PM,能力強的PSM,設計能力優秀的SA.正因為有這樣的人,才能快速的將加入團隊的新成員,融入團隊.
好像具備了上面這些條件想不形成一個好的團隊也是很不容易的事情了:)
posted @
2006-04-08 20:01 混沌中立 閱讀(1039) |
評論 (2) |
編輯 收藏
最近一直在想這個事情,從近幾年的web application的發展情況和工作項目的用戶需求來看,rich client應該又要成為一個潮流了。
主要原因有兩點:
一是網速的提高,過去使用browser是因為過去的網絡速度比較慢,所以只能給客戶端傳送很少的信息量,讓客戶通過網絡傳輸的信息盡可能少的完成操作。而現在網絡速度的提高,讓這樣的要求變少了,網速慢的這個瓶頸也不再存在了。
二是用戶的要求,用戶實際上不在意什么client還是brower,用戶在意的是使用是否方便,響應是否迅速,能否滿足業務需要。如果網速慢,機器配置差的瓶頸在今天的技術條件下不存在了,用戶對于易用性和快速響應的要求就要提高了。這個快速響應不是指那種客戶端<----->服務器的響應,而已一個操作之后快速的出現結果,這個就要求有一部分在C/S模式下在客戶端實現的功能但是在B/S情況下被轉移到服務器上的一些功能需要在客戶端實現。
但是這個rich client不是幾年前的那種臃腫的不行的方式了,應該是比現在的應用的client包含的內容多,但是比以前的client的內容要少。主要解決的問題是,快速響應用戶的操作,讓用戶的操作更簡單。
(思路不是很清晰,暫時是這樣,之后再修改)
posted @
2006-03-17 10:12 混沌中立 閱讀(266) |
評論 (0) |
編輯 收藏