簡體   English   中英

接口還是抽象類?

[英]Interface or abstract class?

對於我的新寵物項目,我有一個設計問題,已經確定了,但我也想要一些其他意見。

我有兩個類(簡化):

class MyObject
{
  string name {get;set;}
  enum relation {get;set;}
  int value {get;set;}
}

class MyObjectGroup
{
  string name {get;set;}
  enum relation {get;set;}
  int value {get;set;}
  List<MyObject> myobjects {get;set;}
}

項目的后期MyObjectGroupMyObject應該同等使用。 為此,我可以采取兩種方式:

  • 創建一個接口: IObject
  • 創建一個抽象類: ObjectBase

我決定采用接口的方式,我后來在代碼中不能每次都寫ObjectBase而只是為了方便IObject - 但是這樣做還有什么其他的IObject呢?

第二,如何在整個故事中添加IXmlSerializable 讓接口繼承IXmlSerializable還是在抽象基類中實現IXmlSerializable有更多的IXmlSerializable

一般來說,我在這種情況下使用的方法是同時擁有一個接口和一個抽象類。 接口定義了接口。 抽象類只是一個幫手。

你真的不能錯過這種方法。 接口使您可以靈活地更改實現。 抽象類為您提供了不必使用的樣板代碼和幫助程序代碼,如果您的方法是根據抽象類明確定義的,那么您將使用它們。

這些是Interfaces和Abstract類之間的一些區別。

1A。 類可以繼承(實現)一個或多個接口。 所以在C#中,接口用於實現多重繼承。
1B。 一個類可以只繼承一個抽象類。

2A。 接口不能提供任何代碼,只能提供簽名。
2B。 抽象類可以提供完整的默認代碼和/或只需要覆蓋的詳細信息。

3A。 接口不能具有子,函數,屬性等的訪問修飾符,所有內容都被假設為公共。
3B。 抽象類可以包含subs,functions,properties的訪問修飾符。

4A。 接口用於定義類的外圍功能。 例如。 ShipCar可以實現IMovable接口。
4B。 抽象類定義類的核心標識,並在那里用於對象。

5A。 如果各種實現僅共享方法簽名,那么最好使用接口。
5B。 如果各種實現具有相同的類型並使用共同的行為或狀態,那么抽象類最好使用。

6A。 如果我們向接口添加一個新方法,那么我們必須跟蹤接口的所有實現並定義新方法的實現。
6B。 如果我們向抽象類添加一個新方法,那么我們可以選擇提供默認實現,因此所有現有代碼都可以正常工作。

7A。 接口不能定義字段。
7B。 抽象類可以定義字段和常量。

8A。 接口不能有構造函數。
8B。 抽象類可以實現默認構造函數。

9A。 接口只能從其他接口繼承。
9B。 抽象類可以從接口,抽象類甚至類繼承。

有理由使用基類之前,接口將是我的默認接口,因為它為我們做出的決策更少。

除非我必須,否則我不會涉及IXmlSerializable ; 它是一個混亂,棘手的界面,往往是一個禍患的原因。

您的序列化要求到底是什么? 有可能是更好的選擇。不過,對於許多串行一個基類比一個接口更容易。 例如,對於XmlSerializer您可以:

[XmlInclude(typeof(MyObject))] // : ObjectBase 
[XmlInclude(typeof(MyObjectGroup))] // : ObjectBase 
public abstract class ObjectBase { /*  */ }

(確切的方法取決於序列化器)

通常,您應該將接口視為某些類型實現的契約 ,將抽象類視為繼承層次結構中不存在的節點 (即派生類和基本抽象類之間存在“是一種”關系)。 但是,在實踐中,您可能需要在其他情況下使用接口,例如在需要多繼承時。

例如, IXmlSerializable本身不是“實體”。 它定義了實體可以實現的合同。 接口位於繼承層次結構的“外部”。

接口將允許您通過提供接口所描述的屬性和方法來定義對象需要實現的“合同”。 您可以通過interface-type的變量引用對象,這可能會導致對提供的內容產生一些混淆。

基類提供了構建繼承“樹”的機會,其中更復雜的類(常見的“類型”)構建在更簡單的“基礎”類的基礎上。 OO中經典又煩人的例子通常是'Shape'的基類,它由Triangle,Square等繼承。

重點是,使用接口,您需要提供與實現它的每個類的完整契約,使用繼承樹(基類),您只需更改/添加子類唯一的屬性和方法,公共屬性和方法保留在基類中。

在上面的示例中,我將'MyObjectGroup'對象繼承基礎'MyObject'類,從這里我可以看到的接口無法獲得。

建築師在設計課程時有兩件事情。

  1. 對象的行為。
  2. 對象的實現。

如果實體具有多個實現,則將對象的行為與其實現分離是可維護性和解耦的關鍵之一。 可以通過抽象類或接口實現分離,但哪一個最好? 讓我們舉一個例子來檢查一下。

讓我們看一個開發場景,其中事物(請求,類模型等)經常變化,您必須提供某些版本的應用程序。

最初的問題陳述 :您必須為印度鐵路創建一個“火車”課程,該課程在1970年具有maxSpeed的行為。

1.使用抽象類進行業務建模

V 0.0(初始問題)初始問題陳述:你必須為印度鐵路創建一個Train類,它在1970年具有maxSpeed的行為。

public abstract class Train {
    public int maxSpeed();
}

V 1.0(改變問題1)改變了問題陳述:你必須在1975年為印度鐵路創建一個具有maxSpeed行為的Diesel Train

public abstract class DieselTrain extends train {
     public int maxFuelCapacity ();
}

V 2.0(更改問題2)登上問題陳述:您必須為印度鐵路創建一個ElectricalTrain類,它具有1980年的maxSpeed,maxVoltage行為。

public abstract class ElectricalTrain extends train {
     public int maxvoltage ();
}

V 3.0(改變問題3)

chanded問題陳述:你必須為印度鐵路創建一個HybridTrain (同時使用柴油和電力)類,它在1985年具有maxSpeed,maxVoltage,maxVoltage的行為。

public abstract class HybridTrain extends ElectricalTrain , DisealTrain {
    { Not possible in java }
}
{here Business modeling with abstract class fails}

2.帶界面的業務建模

只需將abstract單詞更改為interface ......您的業務建模與界面將成功。

http://javaqna.wordpress.com/2008/08/24/why-the-use-on-interfaces-instead-of-abstract-classes-is-encouraged-in-java-programming/

接口:如果您的子類應該都實現某組方法/功能,但每個子類都可以自由提供它自己的實現,那么使用接口。

例如,如果要為車輛實現類層次結構,請實現名為Vehicle的接口,該接口具有Color MaxSpeed等屬性和Drive()等方法。 所有兒童類如Car Scooter AirPlane SolarCar等都應該來自這個基礎界面,但提供了車輛暴露的方法和屬性的單獨實現。

- >如果您希望您的子類在多個繼承中使用接口實現多個不相關的功能。

例如,如果您要實現一個名為SpaceShip的類,該類必須具有來自Vehicle的功能以及來自UFO的功能,那么將Vehicle和UFO都作為接口,然后創建一個實現Vehicle和UFO的SpaceShip類。

抽象類:

- >當你有一個要求,你的基類應該提供某些方法的默認實現,而其他方法應該被子類重寫,使用抽象類。

例如,再舉一個上面的Vehicle類的例子。 如果我們希望從Vehicle派生的所有類以固定方式實現Drive()方法,而其他方法可以被子類重寫。 在這種情況下,我們將Vehicle類實現為具有Drive實現的抽象類,同時將其他方法/屬性保留為抽象,以便它們可以被子類重寫。

- >抽象類的目的是提供多個派生類可以共享的基類的通用定義。

例如,類庫可以定義一個抽象類,該抽象類用作其許多函數的參數,並要求程序員使用該庫通過創建派生類來提供自己的類實現。

你實際上可以和BOTH一起去。 ObjectBase為您省去了多次實現公共屬性的麻煩,並為您實現了IObject。 您使用它的任何地方都會引用IObject,以便您以后可以使用模擬進行測試

我寧願去基礎抽象類,因為,理論上(好吧,它只是一個理論,我沒有證明或說任何其他更糟糕的是這個) - 當你想展示時,應該使用接口,對象能夠做某事(比如IComparable - 你表明無論什么實現它,都可以與其他東西進行比較),而當你有2個實例只是共享一些共同點或有1個邏輯父對象時 - 應該使用抽象類。
你也可以使用基類來實現這兩種方法,它將實現一個接口,它將明確指出你的類可以做什么。

在其他條件相同的情況下,請使用界面。 更容易模擬單元測試。

但一般來說,我所有使用基類的是當我有一些常見的代碼放在一個地方時,而不是派生類的每個實例。 如果它與你所描述的類似,它們的使用方式是相同的,但它們的底層機制是不同的,界面聽起來更合適。

我一直在我的項目中使用抽象類,但在將來的項目中,我將使用接口。 “多重繼承”的優點非常有用。 無論是在代碼中還是為了測試目的,都能夠提供全新的類實現,我們總是受歡迎。 最后,如果您將來希望能夠通過外部開發人員自定義代碼,則無需向他們提供真正的代碼 - 他們只需使用接口即可...

如果你在類中有函數,你應該使用abstact類而不是接口。 通常,接口用於代表類型。

請注意,您無法覆蓋接口中的運算符。 就我而言,這是他們唯一真正的問題。

選擇接口和抽象類不是一個或兩個命題。 如果您需要更改設計,請將其設為界面。 但是,您可能具有提供某些默認行為的抽象類。 抽象類是應用程序框架內的優秀候選者。

抽象類讓你定義一些行為; 他們強迫你的子類提供其他人。 例如,如果您有應用程序框架,則抽象類可以提供默認服務,例如事件和消息處理。 這些服務允許您的應用程序插入您的應用程序框架。 但是,有一些特定於應用程序的功能,只有您的應用程序才能執行。 此類功能可能包括啟動和關閉任務,這些任務通常取決於應用程序。 因此,抽象基類可以聲明抽象關閉和啟動方法,而不是嘗試自己定義該行為。 基類知道它需要那些方法,但是抽象類允許你的類承認它不知道如何執行這些操作; 它只知道它必須啟動行動。 在啟動時,抽象類可以調用啟動方法。 當基類調用此方法時,Java會調用子類定義的方法。

許多開發人員忘記了定義抽象方法的類也可以調用該方法。 抽象類是創建計划繼承層次結構的絕佳方法。 對於類層次結構中的非葉類,它們也是一個很好的選擇。

抽象類的定義可以描述代碼和狀態,從它們派生的類可能不會同時從其他類派生。 這就是技術上的差異。

因此,從使用和哲學的角度來看,不同之處在於,通過設置抽象類,您可以約束該類的對象可以實現的任何其他功能,並為這些對象提供任何常見的基本功能。這樣的對象(這也是一種約束),而通過設置接口,您不需要為其他功能設置約束,也不會為您考慮的功能提供實際代碼。 當你知道這個類的對象應該為了用戶的利益而應該做的所有事情時,使用抽象類。 當對象也可能做其他你甚至無法猜到的事情時,使用接口。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM