乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      策略模式的應(yīng)用實(shí)踐

       ljjzlm 2006-08-04

      策略模式的應(yīng)用實(shí)踐

      Filed under: — bruce zhang @ 5:11 pm

      在工作中,我需要寫一個(gè)話單轉(zhuǎn)換工具,在寫這個(gè)工具的過程中,發(fā)現(xiàn)整個(gè)實(shí)現(xiàn)恰恰可以說是策略模式最好的體現(xiàn)。用這個(gè)例子來說明策略模式的應(yīng)用,最合適不過了。

      話單轉(zhuǎn)換工具的目的:將某個(gè)服務(wù)提供商的話單文本文件,轉(zhuǎn)換為另一個(gè)服務(wù)提供商的話單文本文件。如將聯(lián)通的話單格式轉(zhuǎn)換為移動的話單格式。

      話單轉(zhuǎn)換工具的要求:能夠?qū)崿F(xiàn)多個(gè)服務(wù)提供商話單文本文件的互相轉(zhuǎn)換。

      我們首先來分析一下任務(wù)。首先,各種服務(wù)提供商的話單格式,無疑是不相同的。例如,話單采集后,字段的順序,字段的寬度以及字段間的分割符,都不相同。但是,從總體上來說,話單的表現(xiàn)形式是大致相同的,這位我們實(shí)現(xiàn)話單轉(zhuǎn)換提供了一個(gè)技術(shù)上可行的前提。

      所謂話單轉(zhuǎn)換,就是需要將一個(gè)話單文本文件讀出,然后對每一行的字符串進(jìn)行識別后,再轉(zhuǎn)換為符合相對應(yīng)的服務(wù)提供商標(biāo)準(zhǔn)的話單文本文件。操作很簡單,就是文本文件的讀寫而已,不同的就在于轉(zhuǎn)換的方法。根據(jù)服務(wù)提供商標(biāo)準(zhǔn)的不同,我們應(yīng)該為每一種轉(zhuǎn)換提供相對應(yīng)的算法。而所謂策略模式,正是對算法的包裝和抽象,將算法的責(zé)任和其本身分離。所以,我們現(xiàn)在要做的工作就是將轉(zhuǎn)換話單的算法抽象出來。

      根據(jù)工具的要求,話單轉(zhuǎn)換應(yīng)該包括3個(gè)方法:
      1、將文件讀出的一行字符串轉(zhuǎn)換為對應(yīng)的話單對象;
      2、將一種話單對象轉(zhuǎn)換為另一種話單對象;
      3、將話單對象轉(zhuǎn)換為字符串,以方便寫入話單文本文件;

      根據(jù)以上的分析,我們還需要為不同的話單格式建立相應(yīng)的對象。例如:網(wǎng)通、聯(lián)通和移動的話單格式對象分別為:
      public class CNCCdr
      {
       //網(wǎng)通話單格式對象的公共屬性;
      }
      public class CUCCdr
      {
       //聯(lián)通話單格式對象的公共屬性;
      }
      public class CMCdr
      {
       //移動話單格式對象的公共屬性;
      }

      接下來就應(yīng)該實(shí)現(xiàn)話單轉(zhuǎn)換的算法了。首先需要將算法進(jìn)行抽象,而進(jìn)行抽象的最佳選擇莫過于使用接口,例如我們定義一個(gè)用于話單轉(zhuǎn)換的接口ICdrConvert:
      public interface ICdrConvert
      {
      }
      按照如前的分析,在接口中應(yīng)該包括三個(gè)轉(zhuǎn)換方法。但是現(xiàn)在有個(gè)問題,就是轉(zhuǎn)換的話單對象。由于方法在接口中,為一個(gè)抽象。而話單對象可能有多種,具體轉(zhuǎn)換為何種話單對象,需要到具體實(shí)現(xiàn)時(shí)才能決定。因此,在接口方法中,無論返回類型,還是傳入?yún)?shù),涉及到的話單對象只能是抽象的。也許我們可以考慮System.Object來表示話單對象,但更好的辦法是為所有的話單對象也提供一個(gè)抽象接口。

      由于從目前的分析來看,抽象話單對象并沒有一個(gè)公共的方法,所以這個(gè)抽象的話單接口,是一個(gè)標(biāo)識接口:
      public interface ICdrRecord
      {
      }

      現(xiàn)在,可以對轉(zhuǎn)換接口的方法進(jìn)行定義了:
      public interface ICdrConvert
      {
       ICdrRecord Convert(string record);
       ICdrRecord Convert(ICdrRecord record);
       string Convert(ICdrRecord record);
      }

      自然,這樣的接口定義無法通過編譯。為什么呢?是因?yàn)榈诙€(gè)方法的簽名與第三個(gè)方法的簽名重復(fù)了(方法的簽名和返回類型無關(guān))。因此,我們需要為第三個(gè)方法修改名字。

      但我們仔細(xì)想想,第三個(gè)方法的轉(zhuǎn)換在轉(zhuǎn)換接口中是必要的嗎?該方法的任務(wù)是將一個(gè)話單對象轉(zhuǎn)換為string類型。實(shí)際上這個(gè)責(zé)任,并不需要專門的轉(zhuǎn)換對象來完成,而應(yīng)屬于話單對象本身的責(zé)任。再想想.Net中,所有對象均派生于System.Object,而object類型均提供了ToString()方法。

      從設(shè)計(jì)的角度來看,最好的辦法,是在具體的話單對象中override System.Object的ToString()方法,而不是在轉(zhuǎn)換對象中,提供該轉(zhuǎn)換算法。

      不過考慮到,在話單處理中,更多地會調(diào)用抽象話單接口類型的對象,也許我們將ToString()方法抽象到接口ICdrRecord中會更好。
      public interface ICdrRecord
      {
       string ToString();
      }
      public interface ICdrConvert
      {
       ICdrRecord Convert(string record);
       ICdrRecord Convert(ICdrRecord record);
      }
      而具體的話單對象,就應(yīng)該實(shí)現(xiàn)ICdrRecord接口了。因?yàn)楦髟拞螌ο缶^承了System.Object,則間接地繼承了object對象的ToString()方法,所以,話單對象應(yīng)該重寫該方法:
      public class CNCCdr: ICdrRecord
      {
       //網(wǎng)通話單格式對象的公共屬性;

       //重寫object的ToString()方法,同時(shí)也實(shí)現(xiàn)了接口ICdrRecord的ToString()方法;
       public override string ToString()
       {
        //實(shí)現(xiàn)具體的內(nèi)容;
       }
      }
      public class CUCCdr: ICdrRecord
      {
       //聯(lián)通話單格式對象的公共屬性;

       //重寫object的ToString()方法,同時(shí)也實(shí)現(xiàn)了接口ICdrRecord的ToString()方法;
       public override string ToString()
       {
        //實(shí)現(xiàn)具體的內(nèi)容;
       }
      }
      public class CMCdr: ICdrRecord
      {
       //移動話單格式對象的公共屬性;

       //重寫object的ToString()方法,同時(shí)也實(shí)現(xiàn)了接口ICdrRecord的ToString()方法;
       public override string ToString()
       {
        //實(shí)現(xiàn)具體的內(nèi)容;
       }
      }

      下面就是關(guān)鍵的實(shí)現(xiàn)了。由于我們已經(jīng)為轉(zhuǎn)換算法進(jìn)行了抽象,因此根據(jù)策略模式來實(shí)現(xiàn)具體的轉(zhuǎn)換算法,就是水到渠成的事情了。實(shí)現(xiàn)代碼之前,先來看看UML類圖:

      tragedy.gif

      注意看橙色部分,這一部分即為策略模式的主體。接口ICdrConvert為抽象策略角色,類CNCToCUC,CUCToCM為具體策略角色,它們分別實(shí)現(xiàn)了將網(wǎng)通話單轉(zhuǎn)換為聯(lián)通話單,聯(lián)通話單轉(zhuǎn)換為中國移動話單的算法。根據(jù)實(shí)際需要,還可以添加多個(gè)類似的具體策略角色,并實(shí)現(xiàn)ICdrConvert接口:
      public class CNCToCUC:ICdrConvert
      {
       public ICdrRecord Convert(string record)
       {
        //實(shí)現(xiàn)具體的轉(zhuǎn)換算法;
       }
       public ICdrRecord Convert(ICdrRecord record)
       {
        //實(shí)現(xiàn)具體的轉(zhuǎn)換算法;
       }
      }
      類CUCToCM的實(shí)現(xiàn)相似,不再重復(fù)。

      那么通過策略模式實(shí)現(xiàn),究竟有什么好處呢?請大家注意上圖的CdrOp類。該類是抽象類,它提供了一個(gè)構(gòu)造函數(shù),可以傳遞ICdrConvert對象:
      public abstract class CdrOp
      {
       protected ICdrConvert _convert;
       protected string _sourceFileName;
       protected string _targetFileName;
       
       public CdrOp(string soureFileName,string targetFileName,ICdrConvert convert)
       {
        _sourceFileName = soureFileName;
        _targetFileName = targetFileName;
        _convert = convert;
       }

       public abstract void HandleCdr(); 
      }

      類CdrFileOp繼承了抽象類CdrOp:
      public class CdrFileOp
      {
       public override void HandleCdr()  
       {
        Read();
        Write();
       }
       
       private string Read()
       {
        using (StreamReader sd = new StreamReader(_sourceFileName))
        { 
         ICdrRecord cdr = null; 
         string line;
         while ((line = sd.ReadLine()) != null)
         {
          //首先調(diào)用ICdrRecord Convert(string record)方法;
          //再調(diào)用ICdrRecord Convert(ICdrRecord record)方法;
          //至于實(shí)現(xiàn)的是何種轉(zhuǎn)換,由構(gòu)造函數(shù)傳入的ICdrConvert對象決定;
          cdr = _convert.Convert(_convert.Convert(line));
          _list.Add(cdr);      
         }
        }
       }

       private void Write()
       {
        using (StreamWriter sw = new StreamWriter(_targetFileName,true))
        {    
         sw.Write(head.ToString());
         foreach (ICdrRecord record in _list)
         {
          sw.Write(record.ToString());
         }
        }
       }

       private ArrayList _list = new ArrayList();
      }

      這個(gè)類,實(shí)現(xiàn)了抽象類CdrOp的HandleCdr()方法。但具體的實(shí)現(xiàn)細(xì)節(jié)則是在私有方法Read()和Write()中完成的(根據(jù)實(shí)際情況,也可以把Read和Write方法作為公共抽象方法或保護(hù)方法,放到抽象類CdrOp中,而在抽象類CdrOp中具體提供HandleCdr方法的實(shí)現(xiàn),該方法調(diào)用Read和Write方法,這樣就使用了模版方法模式)。

      注意看Read和Write方法中,沒有一個(gè)具體類。不管是話單對象,還是話單轉(zhuǎn)換對象,均是抽象接口對象。尤其是在Read()方法中,調(diào)用了_Convert的Convert方法:
      cdr = _convert.Convert(_convert.Convert(line));
      內(nèi)部的Convert方法,即_convert.Convert(line),是將讀出來的字符串轉(zhuǎn)換為ICdrRecord對象,然后通過調(diào)用_convert.Convert(ICdrRecord record)方法,再將該對象轉(zhuǎn)換為另一種話單格式對象,但類型仍然屬于ICdrRecord。

      那么在這些轉(zhuǎn)換過程中,究竟轉(zhuǎn)換成了哪一種話單格式對象呢?這是由_convert字段來決定的。而這個(gè)對象則是由構(gòu)造函數(shù)的參數(shù)中傳遞進(jìn)來的。

      同樣的道理,在Write()方法中,大家也可以看到為所有話單對象抽象為一個(gè)接口ICdrRecord的好處。通過ICdrRecord調(diào)用ToString()方法,避免了在CdrFileOp中引入具體對象。要知道,程序一旦引入具體對象,則耦合性就高了。一旦需求發(fā)生改變,就需要對編碼進(jìn)行修改。

      有了以上的架構(gòu),客戶端調(diào)用就非常方便了:
      public class Client
      {
       public static void Main()
       {
        string sourceFileName = “c:\CNCCdr.txt”;
        string target1= “c:\CUCCdr.txt”;
        string target2= “c:\CMCdr.txt”;
        
        //將網(wǎng)通話單轉(zhuǎn)換為聯(lián)通話單;
        ICdrOp op1 = new CdrFileOp(sourceFileName,target1,new CNCToCUC());
        op1.HandleCdr();

        //將剛才轉(zhuǎn)換生成的聯(lián)通話單轉(zhuǎn)換為移動話單;
        ICdrOp op2 = new CdrFileOp(target1,target2,new CUCToCM());
        op2.HandleCdr();  
       }
      }

      當(dāng)然,我們還可以引入工廠模式來創(chuàng)建CdrFileOp對象?;蛘邔CdrConvert對象設(shè)置為CdrFileOp的公共屬性,而非通過構(gòu)造函數(shù)來傳入。然而,通過本文,策略模式的精要已經(jīng)體現(xiàn)得淋漓盡致了。

        本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多