程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi程序設計之--慣用法

Delphi程序設計之--慣用法

編輯:Delphi

  Delphi --技巧探索:  
  { No. 1 }
  創建模式窗體的句子:
  class procedure TMyForm.RunForm(AObj1, AObj2: TObject);
  var
  vForm: TMyForm;
  begin
  vForm := TMyForm.Create(Application);
  with vForm do
  Try
   InitForm(AObj1, AObj2);
   ShowModal;
  Finally
   Free;
  end;
  end;
  //*說明:
  通過class聲明的函數,類似與VC中的靜態函數;使用語句:TMyForm.RunForm(vObj1, vObj2);
  其他具體的,參考:Delphi 幫助中的,class 類說明。
  強調這個慣用法,就是為了:
  1、如果此窗體在多處被使用,那麼可以保證統一都調用此段代碼;
  2、如果功能上有所修改,比如:根據ShowModal的返回值不同進行處理,那麼只修改此函數就行了。
  3、程序封裝性好,易於維護和工作交接。*//

{ No. 2 }//Tag 的使用
  窗體工具欄按鈕事件的響應
  procedure TMyForm.RunOperate(ATag: Integer);
  begin
   Case ATag of
   1: MyButton.Color := clRed;
   2: MyButton.Color := clGreen;
   3: MyButton.Color := clBlack;
   end;
  end;
  
  procedure TMyForm.ToolBtnClick(Sender: TObject);
  begin
   RunOperate(TControl(Sender).Tag);
  end;
  
  如果你在某下拉菜單中,也需要執行類似功能則
  procedure TMyForm.MenuItemClick(Sender: TObject);
  begin
   RunOperate(TMenuItem(Sender).Tag);
  end;
  
  //*說明:
  1、結構清晰
  2、相關的信息集中,比較容易查錯、修改和維護
  3、提高程序的適應、擴展能力;比如現在要求不在工具欄按鈕中實現,而要求在不同按鈕中實現,則修改容易。
  建議:每個分類後面只跟一行或不多的幾行代碼,如果代碼比較多,使用過程函數替代。
  比較有意思的是,我經常如下寫:
  Case btnMyButton.Visible of
  { 顯示 } True: ...
  {不顯示} False: ...
  end;    *//

 

{ No. 3 }//事件指針做參數
  //對於列表等的讀取使用事件指針的方式
  type
   TDataSetEvent = procedure (DataSet: TDataSet; AIndex, ACount: Integer) of Object;
  //從 TADOQuery派生而來的類
  procedure TMyADOQuery.EnumRecord(AWhereStr: String; APro: TDataSetEvent);
  begin
   Close;
   SQL.Clear;
   SQL.Add('Select * From Table1');
   if AWhereStr <> '' then
     SQL.Add('Where ' + AWhereStr);
   Open;
   While Not Eof do
   begin
     if Assigned(APro) then APro(Self, RecNo, RecordCount);
     Next;
   end;  
   Close;
  end;
  
  //*說明:
  此方法來自與Window中,枚舉當前所有子窗體的API函數,EnumChildWindow
  1、原則:盡量將數據讀取與數據顯示、數據處理等分離;如:MVC等都是此目的。
  2、程序擴展性增強,如果您原來希望在列表中顯示或處理某列信息,後來改為用ComboBox,則在修改程序時,不在閱讀數據讀取部分,只需要修改信息顯示等即可。又比如,現在要求您在讀取記錄時,用進度條顯示讀取進度等。
  *//

 

{ No. 4 }//常量數組
  
  { 在 No.2 中,實現了如下的內容
  procedure TMyForm.RunOperate(ATag: Integer);
  begin
  Case ATag of
  1: MyButton.Color := clRed;
  2: MyButton.Color := clGreen;
  3: MyButton.Color := clBlack;
  end;
  end;
  }
  //那麼用數組方式實現,則就比較理想了
  procedure TMyForm.RunOperate(ATag: Integer);
  const
   MyButtonColorMax := 3;
   MyButtonColor: array [1..MyButtonColorMax] of TColor = (clRed, clGreen, clBlack);
  begin
  Case ATag of
  1..MyButtonColorMax:  MyButton.Color := MyButtonColor[ATag];
  101:....
  end;
  end;
  
  //*說明:
  對於數組方式使用,只要注意數組的上限或下限使用常量來實現,然後在以後使用中都盡量使用此常量進行數組循環讀取就行了。
  *//

 

{ No. 5 }消息機制 減少類公共函數
  //如何讓一個窗體中,盡量減少公共函數的定義;
  { 比如:要實現一個當前窗體控件的屬性列表窗體,當需要刷新屬性窗體;改變某屬性值;添加新的屬性等;會有很多需要交互的信息。如果我們使用類公共函數,則需要定義很多的公共函數。同時,如果需要進行窗體類型轉換,轉換為目標窗體類型才可以使用公共函數。所以,會遇到兩個單元需要互相包含的情況 }
  //解決方案:
  TfrmMyForm = class(TForm)
   FfrmProperty: TForm;
  end;
  
  ...
   FfrmProperty := TfrmProperty.MyCreate(Application, Self);
  ...
  
  //當需要刷新屬性窗體時
   FfrmProperty.Perform(WD_REFRESHPROPERTYLIST, 0, 0);
  
  TfrmProperty = class(TForm)
  private
   FMyForm: TForm;
   procedure WDREFRESHPROPERTYLIST(var Message: TMessage); message WD_REFRESHPROPERTYLIST;
  public
   constructor MyCreate(Owner: TComponent; AForm: TForm);
  end;
  
  constructor TfrmProperty.MyCreate(Owner: TComponent; AForm: TForm);
  begin
   inherited Create(Owner);
   FMyForm := AForm;
  end;
  
  //* 對於使用消息的方式,可以減少窗體公共函數的定義。同時,提高程序的可擴充性。如果,使用他的窗體替代時,則可以比較輕松的轉換,因為如果最多也就是您的窗體,對當前的消息沒有進行處理而已 *)// 

 

 

{ No. 6 }使用注冊列表管理可能擴充的模塊
  //項目:要求你對一個數據集支持多種輸出顯示方式
  
  ...例子,以後給出
  
  //* 說明:
  1、“多種輸出方式”,說明輸出方式在今後的應用中可能會經常擴充,因此要在程序設計時考慮到輸出方式的易擴充性。
  2、參考VCL中,控件注冊(RegisterComponents)的機制,可以發現VCL中大量的使用到了注冊機制;其中比較經典的就是控件屬性編輯器的注冊了。
  *//

  { No. 7 }使用預定義控制程序版本
  
  //如果您做的是一個二次開發平台的程序,則必須涉及到產品版本控制和項目版本控制問題
  //通常使用預定義的方式控制
  
  //語句比較簡單了就是:
  {$DEFINE JOYYUAN97}
  {$IFDEF JOYYUAN97} {ELSE} {ENDIF}
  {$UNDEF JOYYUAN97}
  
  *說明:
  1、將預定義劃分在多個單獨的文件中。
  2、在每個單元的最前頭但在Unit 後,使用{$I ...} 將文件包含(Include)進當前單元
  3、根據預定義情況控制當前單元所能包含的單元文件
  4、盡量單獨劃分一個針對項目的預定義文件在包含所有預定義文件後,包含此文件,則在此文件中,可以針對項目的需要,將取消部分預定義{$UNDEF JOYYUAN97}
  *//
  

{ No. 8 } 使用函數指針,減少單元項目包含
  
  //我經常的認為減少單元的包含,是做公共單元的第一步,所以在如何盡量減少單元包含
  //也就是如何減少程序單元的耦合性上,應多下工夫。
  
  { 情景描述:
  TMyFormManager: 窗體管理類
  TMyForm:數據窗體基礎類
  TMyFormAccess:窗體信息保存和讀取類。將窗體信息保存到數據庫或其他什麼類型的結構中
  分析:
  1、窗體基礎類(TMyForm) 和 窗體管理類(TMyFormManager)需要在一個單元 uManagers中實現。
  2、窗體具體實現類(TMyImageForm)單元 fMyImange 需要包含單元uManagers,進行窗體繼承,和窗體管理。
  3、窗體數據讀取類(TMyForMaccess)單元 uMyAccess 需要包含單元uManagers和單元fMyImange
  問題:
   如果我希望實現窗體保存,那麼應該在窗體的某個按鈕事件中實現。則涉及到窗體單元需要包含窗體數據訪問類單元,而如果放在窗體基礎類中,則單元uManager又必須包含單元uMyAccess。
   當數據訪問,即數據存儲格式會根據要求而改變並要求可擴充時,則單元包含必定是一個隱患。
  解決辦法:使用函數指針變量。
  1、在單元uManagers中定義一個,保存數據信息的函數指針變量。
  2、在應用程序初始化的時候給這個函數指針變量賦值。
  3、在需要保存窗體信息時,判斷如果指針不為空,則執行函數保存窗體信息。
  
  

{ No. 9 } 常量,認識常量,使用常量
  有很多書都都介紹了常量定義的重要性,我也會經常想到,但是看看VCL源碼才知道,自己忽略了,別人對常量的使用情況。
  
  1、我們經常使用的消息的定義就是:聲明一個常量,然後在適當的時候使用之。
   通常定義和使用:
   const
     WD_MyMessage = WM_User + 101;
   type
   TMyForm = class(TForm)
   ...
     procedure WDMyMessage(var message: TMessage); message WD_MyMessage; {響應消息位置}
   end;
   但是,如果您將{響應消息位置}語句改寫為:
     procedure WDMyMessage(var message: TMessage); message WM_User + 101;
   同樣,編譯可以成功,使用也正常。所以,常量定義在Window系統處理和接口中應用非常普遍。
  
  2、在Delphi中,我們定義了顏色變量,clRed, clGreen等,也都是定義的常量,便於以後的使用。通過這個觀察我發現,常量的定義應該是在項目中,可部分復用的,所以,可以定義一個標准常量單元,以便在個項目中,復用定義的常量。

 

   { No. 10 }一個Delphi中,常用到的數組
  
  對TIdentMapEntryd類型的數組定義和使用,Delphi中,有比較完善的實現。
   TIdentMapEntry = record
     Value: Integer;
     Name: String;
   end;
  
  1、數組定義:array[0..ArrMax] of TIdentMapEntry
   可參考:Controls單元中:
   Cursors: array[0..21] of TIdentMapEntry = (
   ...
   );
  2、兩個互相求值得函數: IntToIdent(由Value求Name)和 IdentToInt(由Name求Value);
   具體應用可以參考:IdentToCursor 和 CursorToIdent。
  
  3、應用:a、直接應用此樹組定義方式和數組操縱函數;b、學習函數中,對數組訪問和操縱的方式。c、學習標准的信息訪問函數定義: function IntToIdent(Int: Longint; var Ident: string; const Map: array of TIdentMapEntry): Boolean; 具體返回的信息由參數方式返回回來,至於訪問是否有效,則通過函數的布爾返回值加以判斷。

 

{ No. 11 } 由特例到普通的發現
  我通過對 Cursors 的定義和操作函數的跟蹤發現:
  1、如 { No. 10 }中介紹的,將Cursors的定義和一般操作通用化。
  2、提供 Int 和 Ident互轉化的函數。
  3、提供數組列表信息循讀取的函數: GetCursorValues;其中,使用了 { No. 3 } 中介紹的“事件指針 做參數”讀取列表信息的方法。
  
  { No. 6 } 的補充:
  例子:
  procedure RegisterComponents(const Page: string;
   ComponentClasses: array of TComponentClass);
  begin
   if Assigned(RegisterComponentsProc) then
     RegisterComponentsProc(Page, ComponentClasses)
   else
     raise EComponentError.CreateRes(@SRegisterError);
  end;
  
  解讀:
  1、使用注冊的方式,記錄可使用的控件的類型等。
  3、對於 RegisterComponentsProc 使用了{ No. 8 } 中“使用函數指針,減少單元項目包含”的方法,便於將來程序的擴充,版本的升級等。

 

   { No. 11 }只定義一個公共函數
  //項目描述:現在要實現一個CAD畫圖或Visio系統,要求有好的擴展性和易維護性;
  //並且要求耦合性低,便於,將來系統的部分或擴展後的系統封裝後,直接在今後的項目中使用
  
  設計:
  1、設計一個圖形對象抽象類,在此類中,定義一個抽象函數 CadPerform,函數的參數參照function TControl.Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;
  2、在圖形管理類中,實現一個圖形對象列表的管理,列表中保存的是抽象對象的指針。
  3、對於要對具體類對象進行操縱控制時,只需通過條用CanPerform函數,然後根據當前操作的類別傳入 Msg, 並傳入相應的參數信息。
  
  實現: TCad 為由抽象類繼承下來的第一層控件類
  function TCad.CadPerform(Msg: Cardinal; WParam, LParam: Longint): Longint;
  begin
   Case Msg of
   My_Message1: Result := MyMessage1(WParam, LParam);
   My_Message2: Result := MyMessage2(WParam, LParam);
   end;
  end;
  對於,TPoint繼承自 TCad, CadPerform函數實現如下。
  function TPoint.CadPerform(Msg: Cardinal; WParam, LParam: Longint): Longint;
  begin
   Case Msg of
   My_Message1: Result := MyMessage1(WParam, LParam); //屏蔽了TCad中此操作類型的處理
   My_Message3: Result := MyMessage3(WParam, LParam);
   else Result := inherited CadPerform(Msg, WParam, LParam);
   end;
  end;
  
  *說明:
  因為,我們對圖形對象的操作會非常頻繁,所以我們通過定義一個公共開放的接口函數來實現,類的高封裝性和程序的易維護性、好擴展等性能。
  *//

 

{ No. 12 }
  
  以下是我編程時的要求:(部分信息沒有語言限制)
  //以下的解決方案,幾乎都可以在上面的方法中,找到
  1、減少程序的復雜度。a、減少函數個數,使用Case、Tag方式,學習實現Perform定義方式;b、減少單元嵌套關系,使用消息傳遞方式,減少窗體單元的互相包含。
  2、減少

 

{ No. 13 }使用廣播,實現管理類對管理列表對象的通知
  
  //對於{ No. 12 } 項目描述中,當畫圖的窗體控件屬性或狀態改變時,經常會需要通知所有的圖形對象,進行相應的改變。
  //則如果只定義一個廣播函數,就可以實現父子通知的話,也會提高程序的可重用性、擴展性、易維護性等,使類結構清晰。
  
  //比如:1、在Visio和MapInfo中,如果當前窗體的比例尺(縮放比例)改變時,需要用新的比例尺重畫當前所有的顯示圖形對象。2、當當前窗體默認窗體字體改變後,對於默認使用窗體字體顯示文字信息的圖形對象,他們的文字字體也應該相應的改變。
  
  //解決方案,參考TWinControl中,屬性或狀態改變時,通知所有子Controls的處理機制:
  procedure TWinControl.NotifyControls(Msg: Word);
  var
   Message: TMessage;
  begin
   Message.Msg := Msg;
   Message.WParam := 0;
   Message.LParam := 0;
   Message.Result := 0;
   Broadcast(Message);//廣播當前的變更消息
  end;
  其中:
  procedure TWinControl.Broadcast(var Message);
  var
   I: Integer;
  begin
   for I := 0 to ControlCount - 1 do
   begin
     Controls[I].WindowProc(TMessage(Message));
  //改為:with TMessage(Message) do Cads[I].CadPerform(msg, WParam, LParam);
     if TMessage(Message).Result <> 0 then Exit;
   end;
  end;
  但是,我們處理圖形對象時,可能會直接調用 Cads 的CanPerform公共函數即可

  { No. 14 }需要時,動態創建你的對象

比如:http://www.Delphibbs.com/keylife/iblog_show.ASP?xid=824 中的
  //*******方案二  當需要的時候在創建屬性窗體
  uses
  ...
  fProperty;
  type
  TfrmMyMap = class
    ...
    procedure OnfrmMyMapDestroy(Sender: TObject);
    procedure OnMapGeoSelected(AGeo: TGeometry);
  private
    FfrmProperty: TfrmProperty;
    procedure ShowPropertyForm(aVisible: Boolean);
  public
  end;
  
  procedure TfrmMyMap.ShowPropertyForm(aVisible: Boolean);
  begin
  if Not Assigned(FfrmProperty) then FfrmProperty := TfrmProperty.Create(Application);
  FfrmProperty.Visible := aVisible;
  end;
  
  procedure TfrmMyMap.OnfrmMyMapDestroy(Sender: TObject);
  begin
  if Assigned(FfrmProperty) then FfrmProperty.Free;
  end;
  
  procedure TfrmMyMap.OnMapGeoSelected(AGeo: TGeometry);
  begin
  if Assigned(FfrmProperty) then FfrmProperty.MyRefresh(AGeo);
  end;
  
  這裡說明了:
  1、需要時,動態創建你的對象 FfrmProperty
  2、當前對象釋放時,判斷你的對象的合法性,然後釋放動態創建的對象。

 

   { No. 15 }創建接口還是創建結構
  
  //項目描述:我開發一個表格控件時,如果我將單元格設置為一個Com,則如果表格現實的信息過多的話,則裝載速度無法保證,甚至於有死機的可能。我之所以用Com是為了將來每個單元格的處理和信息都可以在控件外擴展。
  
  我的解決辦法是:對於每個從Cell派生來的控件創建一個實例,通過動態創建若干個結構對象Record來記錄個單元格的信息,如果需要對單元格進行操作,則將結構對象指針賦值給Cell組件,測試結果很令人滿意。
  
  所以,如果需要使用某個Com大量實例的話,盡量管理和維護一個實例,而對於其中的數據可以實行動態創建管理,速度上會有很好的效果。
  另外,盡量聲明一個 pMyInterface = ^IMyInterface 借口指針,參數傳遞或使用時,直接使用接口指針,這樣可以減少調用計數函數_AddInft等,如果操作平凡也可以提高速度的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved