2010-09-27 44 views
8

幾個月前我正在寫一堆代碼,現在我正在向它添加東西。我意識到我寫了一大堆函數,這些函數從一個具有大約2/3的函數抽象類的剩餘1/3虛擬類中下來。Delphi:可維護性虛擬與虛擬摘要

我非常生病視寧度:

function descendent.doSomething() : TList; 
begin 
    inherited; 
end; 

當我有了這個基類:

function descendent.doSomething() : TList; 
begin 
    result := nil; 
end; 

,真捨不得用拉閘:

function descendent.doSomething() : TList; 
begin 

end; 

然後想知道爲什麼不起作用。

我喜歡使用抽象函數,因爲編譯器會讓你知道你是否因爲沒有實現某些函數而容易出現抽象錯誤。

我的問題是,因爲我仍然是一個相對較新的Delphi程序員,並且我從來沒有必要在8年後維護任何東西,是否值得花時間以這種方式prune your code(即刪除剛剛繼承的函數在他們和改變你的基類功能從抽象到具體)

回答

1

如果代碼是非常簡單,你發現它很難閱讀和容易出錯,那麼它可能很難閱讀和容易出錯。 (另一方面,如果代碼是複雜的,你覺得很難閱讀,可能是你缺乏經驗,但不是這樣的)。你現在可能會很好地重構它,而問題在你的腦海中仍然新鮮。

+0

我只是想知道,如果我的耐火材料實際上是耐火材料,或只是干預。大多數情況下,將代碼保持原樣並讓Delphi自動完成我的函數並繼承,讓Delphi在未實現抽象函數時提醒我會更簡單。我改變它的唯一原因是因爲當我添加新的子類時,我正在複製並粘貼一個巨大的界面部分(這只是越來越大, – 2010-09-27 16:24:03

7

它一如既往地依賴於問題。我使用接口來定義這組類的用戶界面。至少當我知道我將有不止一個實現基礎實際類的實現時。例如你可以有這樣的事情:

IAllInterfaced = interface(IInterface) 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllInterfaced_ClassA = class(TInterfacedObject, IAllInterfaced) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllInterfaced_ClassB = class(TInterfacedObject, IAllInterfaced) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

在這裏你沒有共同的祖先。每個類只實現了接口,並且沒有公共基礎類的通用基礎結構。如果實現如此不同以至於它們不共享任何內容,但是他可以自行連接,這是可能的。您仍然需要使用相同的界面,以便您對派生類的用戶保持一致。

第二個選項是:

IAllAbstract = interface(IInterface) 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllAbstract_Custom = (TInterfacedObject, IAllAbstract) 
    private 
    ... 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); virtual; abstract; 
    procedure ImplementMeEverywhere_2(const Params: TParams); virtual; abstract; 
    procedure ImplementMeEverywhere_3(const Params: TParams); virtual; abstract; 
    end; 

    TAllAbstract_ClassA = class(TAllAbstract_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

    TAllAbstract_ClassB = class(TAllAbstract_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

這裏有一個基類中的所有類。在那個類中,你可以有共同的屬性或事件其他類等等。但是所有的過程都被標記爲抽象的,因爲它們不執行任何常見的任務。摘要確保它們將在派生類中實現,但不需要在每個類中實現「FieldA」,只需在「TAllAbstract_Custom」中實現它。這確保使用DRY原則。

最後一個選項是:

IAllVirtual = interface(IInterface) 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllVirtual_Custom = (TInterfacedObject, IAllVirtual) 
    private 
    ... 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); virtual; 
    procedure ImplementMeEverywhere_2(const Params: TParams); virtual; 
    procedure ImplementMeEverywhere_3(const Params: TParams); virtual; 
    end; 

    TAllVirtual_ClassA = class(TAllVirtual_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

    TAllVirtual_ClassB = class(TAllVirtual_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

這裏所有派生類都有一個共同的基礎虛擬程序。這可以確保您不必在派生類級別實現每個過程。您只能覆蓋代碼的某些部分,或者根本沒有。

當然,這都是邊緣案例,它們在甜頭圈的地方還有空間。你可以混合使用這些概念。

只需記住:

  1. 接口是確保你從用戶隱藏實現功能強大的工具,你有一個常見的用法點(接口)。他們還強制使用一些規範,因爲接口需要全面實施。
  2. 摘要是一個很好的工具,因此您不必爲空的存根使用沒有真正需要的過程。另一方面,他們強迫你在派生類中實現它們。
  3. 當你有必須由每個類實現的通用代碼並確保OP和DRY乾淨原則的時候,虛擬就派上用場了。當你有不是每個派生類都有或需要的程序時,他們也是受歡迎的。

對不起,很長的回答,但我不能在這裏給一個簡單的解釋,因爲沒有。這一切都取決於手頭的問題。它是派生類共有多少和它們的實現有多不同之間的一種平衡。

1

是的,修剪你的代碼。

它使你的其他代碼更容易閱讀(如你已經提到的,它會更容易看到什麼方法實際上被覆蓋)。作爲一個附加好處,它將更容易地改變父類中方法的簽名:想象一下,您決定將一個參數傳遞給一個虛擬方法;您對父類進行更改,然後您需要爲從給定父類繼承的每個子類重複相同的更改。那時你不希望被稱爲「繼承」的假覆蓋方法!