爲你的程序錦上添花的五種程序組織形式

形式一."見兔撒鷹".
                                                                                    
   如果有一個類有一個集合成員,可以在爲此成員添加元素時再把具體集合建立起來,以免浪費空間和時間.

例:

 1 public class Company{
 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 

異曲同工的另外一個例子:

 1 public class Singleton{
 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,有時還需要在紙上寫好思路,完畢後還有多方測試,生怕出錯,即使編寫好後其他人維護起來也是小心翼翼的.其實沒必要這麼麻煩.

例.

 1   int start=pageIndex*pageSize;
 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的成員.

傳統寫法: 

 1  List allmembers=company.getMembers();// 取得所有成員
 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的查詢應該是這樣:

1   MemberFilter filter1=new MemberFilter(){
2    public boolean
 accept(Member member) {
3         return member.isMale() && member.getAge()>22
;
4 
   }
5 
  };
6 
  
7   List ls=company.listMembers(filter1);

 

這段代碼成功的把查詢條件作爲一個接口分離了出去,接口代碼如下:

1 public interface MemberFilter{
2  public boolean
 accept(Member member); 
3 }

而類Company增加了這樣一個函數:

 1  public List listMembers(MemberFilter memberFilter){
 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代碼,但應該不妨礙理解):

 1 Dim count1 
 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 

變換如下:
這是個稅率計算的語句段,公式是確定的:稅=月薪*稅率-折扣,稅率又和月薪有關係,月薪越高稅率越高,首先這裏可以歸納出一個基本類:

 1 public class TaxItem{
 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 


然後就是稅計算類:

 1 public class TaxCaculator{
 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 }



調用如下 :

 1                 TaxCaculator taxCaculator=new TaxCaculator();
 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. 製作一個命令的抽象接口.

1 public interface Command{ 
2        public abstract void
 execute(String[] args); 
3 

 

2. 讓每種命令都實現這個接口.

 1 // 命令一 
 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. 將命令防置到命令中心中去
命令中心類的代碼如下:

 1 public class Mediation{ 
 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.執行過程

 1                Mediation mediation = new Mediation(); 
 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的子類類名有特殊要求,即一種命令對應一種子類,子類類名可以由命令的首個參數簡單組合而來,否則還是避免不了分支語句。 

以上五種組織形式,若加以靈活運用,相信能減少一些代碼臭味.
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章