三個操作中,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,善用多態,你的代碼將更具靈活性!