程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 代碼重構——之獲得封裝性DELPHI編碼實例

代碼重構——之獲得封裝性DELPHI編碼實例

編輯:Delphi
代碼重構是獲得結構良好的方法,通過重構,我們在保持功能不變的情況下,改善代碼的質量,提高代碼的復用程度。下面是一個獲得改善代碼質量和獲得封裝性的一個具體的例子。(例子使用Delphi) 代碼功能:        給數據集設(TClIEntDataSet)置過濾器,用戶可以在一個TComboBox中選擇要過濾的字段,然後在一個Tedit框中輸入要過濾的值。如圖一: 最常見的做法就是在TComboBox的Items屬性中硬碼寫入我們數據集中的字段名稱,然後在代碼中加入一大堆case或者if語句在判斷用戶選擇的字段來給數據集設置過濾器。 ……       case ComboBox1.ItemIndex of 0:              ClIEntDataSet.Filtered := False;         ClIEntDataSet.Filter := ' F_CODE = ''' + Edit2.Text + '''';               ClIEntDataSet.Filtered := True; 1:              ClIEntDataSet.Filtered := False;         ClIEntDataSet.Filter := ' F_CHINESE_NAME = ''' + Edit2.Text + '''';               ClIEntDataSet.Filtered := True; ……        end;                 或者用 ….…        if ComboBox1.Text = '物料編碼' then     begin               ClIEntDataSet.Filtered := False;         ClIEntDataSet.Filter := ' F_CODE = ''' + Edit2.Text + '''';               ClIEntDataSet.Filtered := True; end else if ComboBox1.Text = '名稱' then begin ClIEntDataSet.Filtered := False;         ClIEntDataSet.Filter := ' F_CHINESE_NAME = ''' + Edit2.Text + '''';               ClIEntDataSet.Filtered := True;   end …… 這樣的代碼通過硬碼同樣也實現了這個給數據集設置過濾器的功能,滿足了需求,但是上面這段代碼是不靈活的。如果數據集的字段很多就要求編碼人員一個一個字段錄入在Items中,而且在寫case必須核對好順序,不然設置的過濾器就是錯誤的也就很容易由開發人員引入BUG。用if語句時也一樣維護一個大量的if同樣是痛苦的,而且不支持需求變化,當用戶要求改變數據集字段的中文顯示名稱時必須也要記住更改TComboBox. Items中的硬碼數據,如果一旦忘記就會引入BUG。   於是我在第一次重構中,嘗試動態的加載TComboBox. Items中的數據,同時為了實現加載後用戶選擇時實現對照。我在這個查詢FORM中加了一個 私有FFIElds: array[0..20, 0..2] of string; 字段來保存數據集中的字段信息數據。同時實現了一個加載數據的過程:   procedure TFrmSPARealStorageQuery.GetQueryFIElds; var   i, iFIEldsCount: Integer; begin   iFIEldsCount := 0;   with DBGride1.DataSource.DataSet do   begin     for i := 0 to FIElds.Count - 1 do       if FIElds[i].Visible then       begin         FFields[iFieldsCount, 0] := Fields[i].FIEldName;         FFields[iFieldsCount, 1] := FIElds[i].DisplayLabel;         Inc(iFIEldsCount);       end;     ComboBox1.Items.Clear;     for i := 0 to iFIEldsCount - 1 do       ComboBox1.Items.Add(FFIElds[i, 1]);   end; end;   這樣就實現了在運行時動態加載字段信息。這樣我的過濾器設置就變成了這樣的。   if ComboBox1.Text <> '' then begin ClIEntDataSet.Filtered := False;     ClientDataSet.Filter := FFIElds[ComboBox1.ItemIndex, 0] +  '''' + Edit2.Text + '''';        ClIEntDataSet.Filtered := True; end;   本方法無疑增加了代碼的靈活性,同時增加了代碼的復用度,因為代碼很好的隔離了變化的數據。因此只要在另一個也是要實現這種的功能的FORM中增加私有字段FFIElds: array[0..20, 0..2] of string 和使用上面的動態加載數據集字段過程,就可以說方便的實現了重用。但是這種重用並不是很好的,因為我們沒有實現很好的封裝性。導致在你的程序中到處散落有重復的代碼(你常常會通過COPY來獲得這個函數的重用,因為上面的代碼是沒有好的封裝性)。如果有一天你要修改數據裝載函數你就必須到處去找那裡拷貝了該函數——你也得修改散落在其他地方的代碼。於是我進行了再一次的重構,並對代碼進行了進一步的封裝。 代碼如下: unit uDataSetFIEldsInfo; // Description:單元包括 TDataSetFIEldsInfo 類,該類封裝了獲得數據集子段信息。 // 並提供了在combobox列表顯示字段顯示信息和獲得對應子段名稱的方法接口 // Created : wuchhao // Date : 2003.5   interface uses Classes, DBClIEnt, StdCtrls; type   TDataSetFIEldsInfo = class   private     FFIEldsList: TStrings;   public     constructor Create;     destructor Destroy; override;     procedure GetDataSetFields(Source: TClIEntDataSet);     procedure ShowFIEldsInfo(Target: TComboBox);     function GetFIEldsNameByDisplayLabel(DisplayLabel: string): string;   end;   implementation   { TDataSetFIEldsInfo } constructor TDataSetFIEldsInfo.Create; begin   FFIEldsList := TStringList.Create; end;   destructor TDataSetFIEldsInfo.Destroy; begin   FFIEldsList.Free;   inherited; end;   procedure TDataSetFieldsInfo.GetDataSetFields(Source: TClIEntDataSet); var   i: Integer; begin   FFIEldsList.Clear;   with Source do   begin     for i := 0 to FIElds.Count - 1 do       if FIElds[i].Visible then       begin         FFieldsList.Add(FIElds[i].DisplayLabel);         FFieldsList.Add(Fields[i].FIEldName);       end;   end; end;   function TDataSetFieldsInfo.GetFIEldsNameByDisplayLabel(   DisplayLabel: string): string; var   index: Integer; begin   Result := '';   index := FFIEldsList.IndexOf(DisplayLabel);   if index <> -1 then     Result := FFIEldsList.Strings[index+1]  ; end;   procedure TDataSetFieldsInfo.ShowFIEldsInfo(Target: TComboBox); var   i: Integer; begin   Target.Items.Clear;   i:=0;   while i <  FFIEldsList.Count do   begin     Target.Items.Add(FFIEldsList.Strings[i]);     i:= i+ 2;   end; end; end.   單元uDataSetFieldsInfo 封裝了與實現本文所述功能相關的數據和方法,把它們封裝在一個類裡面,從而實現了面向對象設計裡面的 Open - Close 原則。類變成了一個黑盒,於是就可方便的重用(black-box reuse),而不必擔心代碼的重復。同時因為封裝了與功能相關的信息,類的職責定義明確(單職責),並有了足夠合適的粒度和好的封裝性。TdataSetFIEldsInfo 很好的把組合框與變化的數據隔離開來,最終提高了代碼的復用程度,同時減少了FORM類的職責和 magic number硬編碼的量。下面是新的代碼: 首先在FORM中聲明TdataSetFIEldsInfo類的一個引用。 …… 在FORM創建的時候調用: FFieldsInfo := TDataSetFIEldsInfo.Create; FFieldsInfo.GetDataSetFIElds(cdMaster); FFieldsInfo.ShowFIEldsInfo(ComboBox1); 這時候我的過濾器設置就變成了: if ComboBox1.Text <> '' then begin ClIEntDataSet.Filtered := False;     ClientDataSet.Filter := FFieldsInfo.GetFIEldsNameByDisplayLabel(ComboBox1.Text) +  '''' + Edit2.Text + '''';        ClIEntDataSet.Filtered := True; end;   通過調用FfIEldsInfo對象的接口過程來獲得對應的子段名稱。   本文是一個重構代碼的簡單例子,我想上面我實現的這個類還可以有很多種寫法和更好的算法。這裡只是提供一種關於重構代碼的思路,為提高我們的編寫代碼質量和它的可維護性、擴展性,探討OOD編程方式上的思路。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved