国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

三談多態—善用virtual

2019-11-17 04:38:48
字體:
來源:轉載
供稿:網友
版權所有:Nicrosoft
文章來源:東日制作室

  多態性,是一種能給程序帶來靈活性的東西??催^《設計模式》的程序員應該都知道,相當多的模式(幾乎所有)都是依靠多態來實現的,以此給程序提供可擴展、可重用性。在《再談多態——向上映射及VMT/DMT》一文中,提到了多態性是依靠于虛函數/虛方法(即動態綁定)來實現的,也介紹了虛函數/虛方法(virtual)的實現方法。那么本文就來談一下,如何使用virtual、善用virtual來獲取多態性給我們帶來的靈活性。
  
  實例是最好的教材,因此本文還是假設一個需求,寫一個實例來講解。不過,我想沒有必要給出所有源碼,因此在本文中有些實現的代碼會粗略帶過。另外,本文所有代碼均為Object Pascal語言編寫,實現環境為Delphi。
  
  另外,由于“方法(Method)”一詞已經成為Object Pascal的術語,因此,以下稱成員函數都為“方法”。也許C++程序員會不太適應這樣的稱呼(呵呵,我自己也不太適應),見諒吧。
  
  假設我們要編寫一個純文本內的編輯器,也就是記事本(呵呵,別嫌例子老套,記事本程序在相當多方面都是很好的教材),編輯控件我們一般會用TMemo或TRichEdit,但是它們的功能都不甚強大,也許我們目前沒有,但日后會找到一個更好的第三方文本編輯控件(比如,支持語法著色的)。因此,我們必須為未來的改進留下方便之門,否則到時候再重寫全部程序真是太傻了。
  
  界面層(菜單響應、狀態顯示等)對文本編輯器控件的控制的代碼對于所有編輯器來說都是類似的,應該可以被重用。那么就必須將界面層的代碼與編輯器控件的控制代碼隔離開來。
  
  如何隔離?我們可以為所有的編輯器控件指定一個公共的接口(抽象類),界面層只看得到這個接口,只使用接口提供的功能,那么,我們更換任何編輯器控件時,都不必更改界面層的代碼了。
  
  首先,抽象出編輯器的基本操作,如:Load(打開文本)、Save(保存到文件)、Copy(復制到剪貼板)等等,將這些操作作為public方法。其次,考慮這些操作中有哪些會涉及到具體相關控件的,對于這些操作,你有兩種選擇:1、假如完全依靠控件本身的,可以選擇將其定義為虛方法或抽象虛方法(即C++中的純虛函數);2、假如只是有部分代碼依靠控件本身的,將這部分操作抽象到一個PRotected的抽象虛方法中,而將相同的部分代碼寫在基類中,并由基類的方法中調用protected的抽象虛方法。
  
  假如還沒有完全明白,請看代碼:

TEditor = class // 抽象基類
  private
   m_FileName : String;
  protected
   function DoLoad(FileName : String) : Boolean; virtual; abstract;
public
function Load(FileName : String) : Boolean;
function Save() : Boolean;
function SaveAs(FileName : String) : Boolean; virtual; abstract;
   // ... 其他需要的操作,由需求而定
end;
  
  好,我們來具體說明一下TEditor為什么是這個樣子的。其有一個私有成員,保存編輯器所對應的文件名。然后看public部分,它至少提供了三個操作:Load——從某文件中讀取文本到編輯器;Save——將文本保存到文件,文件名為m_FileName所保存的;SaveAs——以指定的一個文件名保存文本。

  三個操作中,SaveAs為抽象虛方法,因為它的實際動作與每個編輯控件相關,而基類本身并不知道該如何保存文件。

  Save和Load都是非虛方法,其中Save的任務很簡單,就是將文本以m_FileName所保存的文件名保存,其實現可以是調用抽象的SaveAs,只需要將文件名傳給SaveAs即可?;愅耆梢源_定如何完成Save(因為它可以保證派生類實現了SaveAs),因此其為非虛方法。

  而Load呢?Load操作的步驟是:1、將文本從文件中讀取到編輯器控件中;2、將m_FileName的值設置為所讀取的文件名。其中第一個步驟與具體編輯器控件相關,應該是由派生類去實現它,第二個步驟派生類無法實現,因為派生類看不到私有的m_FileName成員。但即使把m_FileName移到protected節中,以使派生類可以訪問它,也并非好辦法,因為這樣的話,在實現每個派生類時,都要記住設置m_FileName的值,這不僅造成代碼重復(每個派生類都要這樣做),而且這不應該是派生類的義務。因為m_FileName應該是基類的內部實現,對外不可見。因此,第二個步驟應該由基類來完成。如何達成這個目的呢?
  我們可以注重到,protected節中有一個DoLoad方法,它就被用來完成第一個步驟——每個編輯器控件去將文本讀入編輯器。然后,DoLoad由Load方法中被選擇在適當的時機調用。
  
基類的實現如下:

function TEditor.Load(FileName : String) : Boolean;

  begin
   Result := DoLoad(FileName);
   if Result then
   m_FileName := FileName;
end;
  
function TEditor.Save() : Boolean;
  begin
   SaveAs(m_FileName); // 調用抽象的 SaveAs
end;
  
  接著,我們使用TMemo來實現一個編輯器類:
  
  TMemoEditor = class(TEditor)
  private
   m_Editor : TMemo;
  protected
   function DoLoad(FileName : String) : Boolean; override;
public
   constrUCtor Create();
   destrcutor Destroy(); override;
  
   function SaveAs(FileName : String) : Boolean; override;
   // ...其它需要的操作
  end;   
  在該派生類中,有一個私有成員,即TMemo控件的實例。然后覆蓋(override)了基類的兩個抽象虛方法:DoLoad和Save。
  
  其實現如下:

  function TMemoEditor.Create();
  begin
   // 創建TMemo實例
m_Editor := TMemo.Create(nil);
  
// 接著完成將TMemo實例置于界面上顯示出來等操作,省略
end;
  
  function TMemoEditor.Destroy();
  begin
   // 其他清理工作
  
   m_Editor.Free();
   m_Editor := nil;
  end;
  
  function TMemoEditor.DoLoad(FileName : String) : Boolean;
  begin
Result := false;
try
   m_Editor.LoadFromFile(FileName);
  except end;
   Result := true;
  end;
  
  function TMemoEditor.SaveAs(FileName : String) : Boolean;
  begin
   Result := false;
   try
   m_Editor.SaveToFile(FileName);
   except end;
   Result := true;
end;
  
  很好,這樣的實現已經可以使個部分運作正常了。假如,今后找到更好的編輯器控件,只需要從TEditor派生,再實現一個TXXXEditor類即可,其他部分的代碼不用作任何改動。而且,具體實現的TXXXEditor類中的代碼,只和具體控件本身特性相關(如:讀取、保存文件的方法),而公共邏輯也已經在TEditor類中實現了。
  
  virtual的使用方法,基于筆者個人熟悉與經驗:
  
  1、假如基類不知道如何實現某方法(只有派生類知道),而基類的其他方法又必須使用該方法,則把該方法聲明為抽象虛方法—— virtual; abstract;(即C++的純虛函數)。
  
  2、假如基類能夠為某方法提供一種默認實現,但派生類可能完全重寫這個實現,則將該方法聲明為虛方法—— virtual;并實現默認算法。

  3、假如基類能夠且必須提供某方法的部分的實現,而派生類必須提供另一部份的實現,則將該方法聲明為非虛方法,并在基類中為其配套提供一個虛方法或抽象虛方法,以答應由基類本身調用和被派生類覆蓋。如同上例中的Load與DoLoad。
  
  善用virtual,善用多態,你的代碼將更具靈活性!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 吉水县| 泸西县| 广昌县| 曲水县| 洞口县| 观塘区| 千阳县| 石柱| 和林格尔县| 遂平县| 梅州市| 府谷县| 祥云县| 驻马店市| 遂昌县| 苏尼特右旗| 融水| 毕节市| 延安市| 枣庄市| 镇宁| 七台河市| 弥渡县| 宜兰市| 舒城县| 旬邑县| 沙湾县| 静安区| 郓城县| 苏尼特右旗| 石首市| 汨罗市| 郯城县| 资兴市| 潼南县| 邵东县| 会宁县| 宜川县| 交口县| 连州市| 巴林左旗|