Picses' sky

          Picses' sky
          posts - 43, comments - 29, trackbacks - 0, articles - 24

          轉(zhuǎn)自:http://www.aygfsteel.com/sitinspring/archive/2007/06/05/122119.aspx
          作者:sitinspring


          形式一."見(jiàn)兔撒鷹".
          如果有一個(gè)類有一個(gè)集合成員,可以在為此成員添加元素時(shí)再把具體集合建立起來(lái),以免浪費(fèi)空間和時(shí)間.

          例:

           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 


          異曲同工的另外一個(gè)例子:

           1 public class Singleton{
           2        private static Singleton instance=null;
           3 
           4        public static synchronized Singleton getInstance(){
           5    // 要用的時(shí)候再把Singleton建立起來(lái)
           6    if(instance==null){
           7      instance=new Singleton();
           8    }       
           9 
          10    return instance;
          11        }
          12 }
          13 

           

          形式二."只管結(jié)果,不顧過(guò)程"
          從集合中取區(qū)間元素時(shí),可以直接從上下限之間取來(lái),讓try...catch...去處理越界的事.

          題設(shè):有個(gè)數(shù)不定元素的列表(allTodoes),需要從中取N個(gè),起始位置不限,你怎么編寫(xiě)程序.

          很多人開(kāi)始進(jìn)行越界的判斷,出來(lái)一堆if else,有時(shí)還需要在紙上寫(xiě)好思路,完畢后還有多方測(cè)試,生怕出錯(cuò),即使編寫(xiě)好后其他人維護(hù)起來(lái)也是小心翼翼的.其實(shí)沒(méi)必要這么麻煩.

          例.

           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   }

          題外話:分支和循環(huán)語(yǔ)句天生就不容易理解,尤其是在嵌套較深的時(shí)候,因?yàn)檫@是機(jī)器的思維特性.還是try...catch...比較貼近人類思維.


          形式三."奉旨行事"
          在查詢中,如果把查詢條件和遴選過(guò)程分開(kāi)來(lái)更有益,程序也因此解耦合.這才是OO化的查詢.

          需求:從公司的職員列表中,找出男性且年齡大于22的成員.

          傳統(tǒng)寫(xiě)法: 

           1  List allmembers=company.getMembers();// 取得所有成員
           2   List results=new ArrayList();// 結(jié)果列表
           3   
           4   for(Iterator it=allmembers.iterator();it.hasNext();){
           5    Member member=(Member)it.next();
           6    
           7    if(member.getAge()>22 && member.isMale()){  // 篩選,這里是把查詢條件和遴選過(guò)程融合在一起,條件一變立即就得加個(gè)分支.
           8     results.add(member);
           9    }
          10   } 
          11 

           

          這種寫(xiě)法沒(méi)有錯(cuò),但是不是面向?qū)ο蟮膶?xiě)法,它有以下缺陷:
          1.查詢條件和篩選過(guò)程沒(méi)有分離.
          2.這樣寫(xiě)的后果使Company變成了一個(gè)失血模型而不是領(lǐng)域模型.
          3.換查詢條件的話,上面除了"篩選"一句有變化外其它都是模板代碼,重復(fù)性很高.

          真正符合OO的查詢應(yīng)該是這樣:

          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);

           

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

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

          而類Company增加了這樣一個(gè)函數(shù):

           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  }

          這就把模板代碼歸結(jié)到了類內(nèi)部,外面不會(huì)重復(fù)書(shū)寫(xiě)了.Company也同時(shí)擁有了數(shù)據(jù)和行為,而不是原來(lái)的數(shù)據(jù)容器了.

          形式四."化繁為簡(jiǎn)"
          這種結(jié)構(gòu)將多個(gè)分支語(yǔ)句變換為一個(gè)查表結(jié)構(gòu),這樣做對(duì)擴(kuò)充程序結(jié)構(gòu),修改具體數(shù)額都很方便,使程序更易于維護(hù).還可以把歸結(jié)出的表結(jié)構(gòu)放在持久介質(zhì)中如XML文件,數(shù)據(jù)庫(kù)等,用到的時(shí)候再取,這樣做在條件變化時(shí)不需要修改程序.

          原始代碼(VB代碼,但應(yīng)該不妨礙理解):

           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 

           

          變換如下:
          這是個(gè)稅率計(jì)算的語(yǔ)句段,公式是確定的:稅=月薪*稅率-折扣,稅率又和月薪有關(guān)系,月薪越高稅率越高,首先這里可以歸納出一個(gè)基本類:

           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 


          然后就是稅計(jì)算類:

           1 public class TaxCaculator{
           2         private static ArrayList list=new ArrayList();
           3         
           4         public TaxCaculator(){
           5                 // 這里把各個(gè)等級(jí)加入到鏈表中,注意添加順序是由小到大
           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         // 這個(gè)函數(shù)用來(lái)計(jì)算所得稅
          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                 // 返回最終結(jié)果,當(dāng)然,這個(gè)公式也可以放在TaxItem 類中,這里就見(jiàn)仁見(jiàn)智了。
          30                 return salary*item.getRatio()-item.getDiscount();
          31         }
          32 }



          調(diào)用如下 :

           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));


           

          形式五."分而治之"
          該結(jié)構(gòu)將分支語(yǔ)句的執(zhí)行部分分散到單獨(dú)的類中處理,降低了系統(tǒng)耦合度,程序也更容易維護(hù).

          舉例如下:
          在日常工作中,我們經(jīng)常需要解析一段字符串并交由相應(yīng)的函數(shù)進(jìn)行處理,例如TCP/IP通信中的命令解析和用戶自定義文件解析等場(chǎng)合,通常的處理方法是這樣:

          if(命令==”AAA”){
              函數(shù)AAA執(zhí)行;
          }
          else if(命令==”BBB”){
              函數(shù)BBB執(zhí)行;
          }
          .
          .
          .
          else{
              函數(shù)XXX執(zhí)行;
          }

          這種方法在命令較少時(shí)是有效的,當(dāng)命令眾多時(shí),if語(yǔ)句和相關(guān)的函數(shù)將會(huì)形成一個(gè)巨集,給檢查,維護(hù)和擴(kuò)充帶來(lái)了很大的不便,久而久之將會(huì)成為系統(tǒng)性能提升的瓶頸。

          一個(gè)成功的軟件程序必須盡可能簡(jiǎn)單并易于重構(gòu)和擴(kuò)展,在命令模式和Java反射機(jī)制的幫助下,我們可以從容解決上述問(wèn)題,達(dá)到簡(jiǎn)單并易于重構(gòu)和擴(kuò)展的要求。以下將簡(jiǎn)要說(shuō)明解決方案。

          1. 制作一個(gè)命令的抽象接口.

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

           

          2. 讓每種命令都實(shí)現(xiàn)這個(gè)接口.

           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 


          讓每種命令都實(shí)現(xiàn)execute接口的用意是強(qiáng)制每個(gè)命令的執(zhí)行方式一致,簡(jiǎn)化調(diào)用時(shí)的處理,但執(zhí)行內(nèi)容應(yīng)該根據(jù)實(shí)際情況決定.
          例如
          命令一的執(zhí)行內(nèi)容是輸出參數(shù)的個(gè)數(shù)
          命令二的執(zhí)行內(nèi)容是輸出參數(shù)的內(nèi)容
          命令二的執(zhí)行內(nèi)容是輸出最后一個(gè)參數(shù)

          3. 將命令防置到命令中心中去
          命令中心類的代碼如下:

           1 public class Mediation{ 
           2        Command cmmd;// 命令對(duì)象的引用 
           3        String[] cmmdArgs;// 參數(shù)列表 
           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];// 根據(jù)分析后命令的第一個(gè)參數(shù)得到類名 
          13                 
          14                try
          15                        Class cls=Class.forName(className);// 利用反射機(jī)制得到類 
          16                        cmmd=(Command)cls.newInstance();// 由類得到類實(shí)例 
          17                } 
          18                catch(Exception ex){ 
          19                        ex.printStackTrace(); 
          20                }                
          21        }        
          22 
          23         
          24        public void executeCommand(){ 
          25                cmmd.execute(cmmdArgs);// 執(zhí)行命令對(duì)象的execute方法 
          26        } 
          27 
          28 


          4.執(zhí)行過(guò)程

           1                Mediation mediation = new Mediation(); 
           2                // 取得命令一并執(zhí)行 
           3                mediation.fetchCommand("Type01  AB CD"); 
           4                mediation.executeCommand(); 
           5 
           6                // 取得命令二并執(zhí)行 
           7                mediation.fetchCommand("Type02  1 2 3 4"); 
           8                mediation.executeCommand(); 
           9 
          10                // 取得命令三并執(zhí)行 
          11                mediation.fetchCommand("Type03  USA  Russia China"); 
          12                mediation.executeCommand(); 
          13 

          執(zhí)行效果如下:
          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!

          由上可見(jiàn),我們使用反射機(jī)制消除了龐大的分支語(yǔ)句,把命令的執(zhí)行過(guò)程分散到了Command的各個(gè)子類中,降低了命令類和控制中心類的耦合程度,達(dá)到了簡(jiǎn)單并易于重構(gòu)和擴(kuò)展的要求。如果新增一種命令,只需增加Command的一個(gè)子類就可以了。
          很多情況下命令的execute函數(shù)需要命令中心類或者其它類的信息,這時(shí)可以在Command接口類和Command的子類中間添加一個(gè)類CommandBase,在其中包含一個(gè)命令中心類或者其它類的引用,并增加相應(yīng)的getter/setter函數(shù),Command的子類繼承這個(gè)類并實(shí)現(xiàn)Command的接口即可,最后在fetchCommand函數(shù)中傳入中心類或者其它類的引用即可。

          注意:這里對(duì)命令和Command的子類類名有特殊要求,即一種命令對(duì)應(yīng)一種子類,子類類名可以由命令的首個(gè)參數(shù)簡(jiǎn)單組合而來(lái),否則還是避免不了分支語(yǔ)句。 


          以上五種組織形式,若加以靈活運(yùn)用,相信能減少一些代碼臭味.

          Feedback

          # re: 能為你的程序錦上添花的五種程序組織形式[zz]  回復(fù)  更多評(píng)論   

          2007-11-11 21:30 by 曲強(qiáng) Nicky
          第一個(gè)約束下類型吧~呵呵,第二個(gè)不是單例嗎,這里怎么理解的呀
          主站蜘蛛池模板: 台山市| 永安市| 黔西县| 天祝| 奇台县| 天等县| 大足县| 利川市| 图们市| 平潭县| 湾仔区| 宣城市| 商城县| 砚山县| 博客| 深州市| 高清| 北辰区| 兰溪市| 闸北区| 太康县| 砀山县| 青州市| 洞口县| 中牟县| 五常市| 塘沽区| 高陵县| 海丰县| 宁波市| 石林| 宁明县| 奈曼旗| 湟源县| 诸城市| 绥德县| 溧水县| 嵩明县| 茶陵县| 体育| 太仓市|