能為你的程序錦上添花的幾種程序結構(轉)
一."用到再說"結構.
如果有一個類有一個集合成員,可以在為此成員添加元素時再把具體集合建立起來,以免浪費空間和時間.
例:
2 private List members=null;
3
4 public void addMember(Member member){
5 if(members=null){
6 members=new ArrayList();
7 }
8
9 members.add(member);
10 }
11 }
12
異曲同工的另外一個例子:
2 private static Singleton instance=null;
3
4 public static synchronized Singleton getInstance(){
5 // 要用的時候再把Singleton建立起來
6 if(instance==null){
7 instance=new Singleton();
8 }
9
10 return instance;
11 }
12 }
13
二."試試再說"結構
從集合中取區間元素時,可以直接從上下限之間取來,讓try...catch...去處理越界的事.
題設:有個數不定元素的列表(allTodoes),需要從中取N個,起始位置不限,你怎么編寫程序.
很多人開始進行越界的判斷,出來一堆if else,有時還需要在紙上寫好思路,完畢后還有多方測試,生怕出錯,即使編寫好后其他人維護起來也是小心翼翼的.其實沒必要這么麻煩.
例.
2 int end=start+pageSize;
3
4 for(int i=start;i<end;i++){
5 try{
6 todoResult.addTodo((Todo)allTodoes.get(i));
7 }
8 catch(Exception ex){
9 continue;
10 }
11 }
題外話:分支和循環語句天生就不容易理解,尤其是在嵌套較深的時候,因為這是機器的思維特性.還是try...catch...比較貼近人類思維.
三."憑文件領人"結構
在查詢中,如果把查詢條件和遴選過程分開來更有益,程序也因此解耦合.這才是OO化的查詢.
需求:從公司的職員列表中,找出男性且年齡大于22的成員.
傳統寫法:
2 List results=new ArrayList();// 結果列表
3
4 for(Iterator it=allmembers.iterator();it.hasNext();){
5 Member member=(Member)it.next();
6
7 if(member.getAge()>22 && member.isMale()){ // 篩選,這里是把查詢條件和遴選過程融合在一起,條件一變立即就得加個分支.
8 results.add(member);
9 }
10 }
11
這種寫法沒有錯,但是不是面向對象的寫法,它有以下缺陷:
1.查詢條件和篩選過程沒有分離.
2.這樣寫的后果使Company變成了一個失血模型而不是領域模型.
3.換查詢條件的話,上面除了"篩選"一句有變化外其它都是模板代碼,重復性很高.
真正符合OO的查詢應該是這樣:
2 public boolean accept(Member member) {
3 return member.isMale() && member.getAge()>22;
4 }
5 };
6
7 List ls=company.listMembers(filter1);
這段代碼成功的把查詢條件作為一個接口分離了出去,接口代碼如下:
2 public boolean accept(Member member);
3 }
而類Company增加了這樣一個函數:
2 List retval=new ArrayList();
3
4 for(Iterator it=members.iterator();it.hasNext();){
5 Member member=(Member)it.next();
6
7 if(memberFilter.accept(member)){
8 retval.add(member);
9 }
10 }
11
12 return retval;
13 }
這就把模板代碼歸結到了類內部,外面不會重復書寫了.Company也同時擁有了數據和行為,而不是原來的數據容器了.
四."查表"結構
這種結構將多個分支語句變換為一個查表結構,這樣做對擴充程序結構,修改具體數額都很方便,使程序更易于維護.還可以把歸結出的表結構放在持久介質中如XML文件,數據庫等,用到的時候再取,這樣做在條件變化時不需要修改程序.
原始代碼(VB代碼,但應該不妨礙理解):
2 count1 = salary.Value + USA.Value * Drate + JAN.Value * Jrate - 4000
3 If count1 < 500 Then
4 tax.Value = count1 * 0.05
5 ElseIf count1 < 2000 Then
6 tax.Value = count1 * 0.1 - 25
7 ElseIf count1 < 5000 Then
8 tax.Value = count1 * 0.15 - 125
9 ElseIf count1 < 20000 Then
10 tax.Value = count1 * 0.2 - 375
11 ElseIf count1 < 40000 Then
12 tax.Value = count1 * 0.25 - 1375
13 ElseIf count1 < 60000 Then
14 tax.Value = count1 * 0.3 - 3375
15 Else
16 tax.Value = count1 * 0.3 - 3375
17 End If
變換如下:
這是個稅率計算的語句段,公式是確定的:稅=月薪*稅率-折扣,稅率又和月薪有關系,月薪越高稅率越高,首先這里可以歸納出一個基本類:
2 float limit; // 月薪界限
3 float ratio;// 稅率
4 float discount;// 折扣
5
6 public TaxItem(float limit,float ratio,float discount){
7 this.limit=limit;
8 this.ratio=ratio;
9 this.discount=discount;
10 }
11
12 public TaxItem(){
13 this(0.0f,0.0f,0.0f);
14 }
15
16 public float getDiscount() {
17 return discount;
18 }
19
20 public float getLimit() {
21 return limit;
22 }
23
24 public float getRatio() {
25 return ratio;
26 }
27 }
28
29
然后就是稅計算類:
2 private static ArrayList list=new ArrayList();
3
4 public TaxCaculator(){
5 // 這里把各個等級加入到鏈表中,注意添加順序是由小到大
6 list.add(new TaxItem(500.0f,0.05f,0.0f));
7 list.add(new TaxItem(2000.0f,0.1f,25.0f));
8 list.add(new TaxItem(5000.0f,0.15f,125.0f));
9 list.add(new TaxItem(20000.0f,0.2f,375.0f));
10 list.add(new TaxItem(40000.0f,0.25f,1375.0f));
11 list.add(new TaxItem(60000.0f,0.3f,3375.0f));
12 }
13
14 // 這個函數用來計算所得稅
15 public float getTax(float salary){
16 TaxItem item=new TaxItem();
17
18 for(int i=0;i<list.size();i++){
19 item=(TaxItem)list.get(i);
20
21 if(salary>item.getLimit()){
22 continue;
23 }
24 else{
25 break;
26 }
27 }
28
29 // 返回最終結果,當然,這個公式也可以放在TaxItem 類中,這里就見仁見智了。
30 return salary*item.getRatio()-item.getDiscount();
31 }
32 }
調用如下 :
2
3 float salary=1000.f;
4 System.out.println("Salary="+salary +" Tax="+taxCaculator.getTax(salary));
5
6 salary=2000.f;
7 System.out.println("Salary="+salary +" Tax="+taxCaculator.getTax(salary));
8
9 salary=3000.f;
10 System.out.println("Salary="+salary +" Tax="+taxCaculator.getTax(salary));
五."分而治之"結構
該結構將分支語句的執行部分分散到單獨的類中處理,降低了系統耦合度,程序也更容易維護.
舉例如下:
在日常工作中,我們經常需要解析一段字符串并交由相應的函數進行處理,例如TCP/IP通信中的命令解析和用戶自定義文件解析等場合,通常的處理方法是這樣:
if(命令==”AAA”){
函數AAA執行;
}
else if(命令==”BBB”){
函數BBB執行;
}
.
.
.
else{
函數XXX執行;
}
這種方法在命令較少時是有效的,當命令眾多時,if語句和相關的函數將會形成一個巨集,給檢查,維護和擴充帶來了很大的不便,久而久之將會成為系統性能提升的瓶頸。
一個成功的軟件程序必須盡可能簡單并易于重構和擴展,在命令模式和Java反射機制的幫助下,我們可以從容解決上述問題,達到簡單并易于重構和擴展的要求。以下將簡要說明解決方案。
1. 制作一個命令的抽象接口.
2 public abstract void execute(String[] args);
3 }
2. 讓每種命令都實現這個接口.
2 public class CommandType01 implements Command{
3 public void execute(String[] args){
4 System.out.println("\n commandType01 start!");
5 System.out.print("\t commandType01 Length="+args.length);
6 System.out.println("\n commandType01 End!");
7 }
8 }
9
10 // 命令二
11 public class CommandType02 implements Command{
12 public void execute(String[] args){
13 System.out.println("\n commandType02 start!");
14
15 System.out.print("\t commandType02 is:");
16 for(String item:args){
17 System.out.print("\t "+item);
18 }
19
20 System.out.println("\n commandType02 End!");
21 }
22 }
23
24 // 命令三
25 public class CommandType03 implements Command{
26 public void execute(String[] args){
27 System.out.println("\n commandType03 start!");
28 System.out.print("\t commandType03 last Nation="+args[args.length-1]);
29 System.out.println("\n commandType03 End!");
30 }
31 }
32
33
讓每種命令都實現execute接口的用意是強制每個命令的執行方式一致,簡化調用時的處理,但執行內容應該根據實際情況決定.
例如
命令一的執行內容是輸出參數的個數
命令二的執行內容是輸出參數的內容
命令二的執行內容是輸出最后一個參數
3. 將命令防置到命令中心中去
命令中心類的代碼如下:
2 Command cmmd;// 命令對象的引用
3 String[] cmmdArgs;// 參數列表
4
5 public Mediation(){
6
7 }
8
9 public void fetchCommand(String strCmmd){
10 cmmdArgs=strCmmd.split("\s+");// 分析原始命令
11
12 String className="Command"+cmmdArgs[0];// 根據分析后命令的第一個參數得到類名
13
14 try{
15 Class cls=Class.forName(className);// 利用反射機制得到類
16 cmmd=(Command)cls.newInstance();// 由類得到類實例
17 }
18 catch(Exception ex){
19 ex.printStackTrace();
20 }
21 }
22
23
24 public void executeCommand(){
25 cmmd.execute(cmmdArgs);// 執行命令對象的execute方法
26 }
27
28 }
4.執行過程
2 // 取得命令一并執行
3 mediation.fetchCommand("Type01 AB CD");
4 mediation.executeCommand();
5
6 // 取得命令二并執行
7 mediation.fetchCommand("Type02 1 2 3 4");
8 mediation.executeCommand();
9
10 // 取得命令三并執行
11 mediation.fetchCommand("Type03 USA Russia China");
12 mediation.executeCommand();
13
執行效果如下:
commandType01 start!
commandType01 Length=3
commandType01 End!
commandType02 start!
commandType02 is: Type02 1 2 3 4
commandType02 End!
commandType03 start!
commandType03 last Nation=China
commandType03 End!
由上可見,我們使用反射機制消除了龐大的分支語句,把命令的執行過程分散到了Command的各個子類中,降低了命令類和控制中心類的耦合程度,達到了簡單并易于重構和擴展的要求。如果新增一種命令,只需增加Command的一個子類就可以了。
很多情況下命令的execute函數需要命令中心類或者其它類的信息,這時可以在Command接口類和Command的子類中間添加一個類CommandBase,在其中包含一個命令中心類或者其它類的引用,并增加相應的getter/setter函數,Command的子類繼承這個類并實現Command的接口即可,最后在fetchCommand函數中傳入中心類或者其它類的引用即可。
注意:這里對命令和Command的子類類名有特殊要求,即一種命令對應一種子類,子類類名可以由命令的首個參數簡單組合而來,否則還是避免不了分支語句。
以上五種結構如果多加注意,應該能起到錦上添花的效果.
posted on 2007-06-05 13:26 liaojiyong 閱讀(1009) 評論(1) 編輯 收藏 所屬分類: Java