簡體   English   中英

接口和抽象類在解耦方面的區別?

[英]Difference between Interface and Abstract class in terms of Decoupling?

我們知道Interface和Abstract類之間基本上有兩個重要的區別。

  1. 我們可以在抽象類中使用函數定義。 當我們想要在類中添加函數而不需要追蹤它的所有實現時,這是有利的。

  2. 我們可以有多個接口實現。

我剛才知道我們可以在解耦方面區分它們嗎?

你的評論...

另外,如果您能提供一個非常基本的鏈接來解釋接口和抽象類的解耦?

我們通常使用業務邏輯層數據訪問層 (包含抽象函數)和DataAccess.SqlServer層 對? 盡管我們知道業務需求,但為什么我們要創建數據訪問層 (包含抽象函數),為什么業務邏輯層不能直接訪問DataAccess.SqlServer層

解耦

在編程和設計中,這通常是使代碼可以在盡可能少的依賴性下重復使用的行為。

在這種情況下的工廠模式

使用工廠模式時,您有一個集中式工廠,可以創建對象而無需自己定義它們。 這取決於對象的定義。

摘要和界面

接口

定義接口是最佳實踐,因為它允許使用輕量級類型進行推理,並且還提供所有繼承類必須遵守的藍圖。 例如, IDisposable必須實現Dispose方法。 請注意,這與接口分離,因為繼承IDisposable每個類都將定義自己的Dispose方法的函數。

抽象

Abstract類似於接口,因為它用於繼承和推理,但它包含所有類將繼承的定義。 每輛汽車都會有一個引擎,所以汽車的一個好的抽象類可以包括一套預定義的引擎方法。

編輯

說明

在這里,您將看到一個使用接口和抽象類的繼承的簡單示例。 當接口由抽象類繼承然后定制它的方法時,就會發生解耦。 這允許類繼承抽象類,並且仍然具有與接口相同的類型。 優點是當期望的類型是原始接口時,可以使用繼承抽象類的類。

解耦

該優點允許使用符合預期接口的任何實現。 因此,可以編寫和傳入許多不同的重載。這是一個例子。

接口定義

public interface IReady
{
    bool ComputeReadiness();
}

遺產

public abstract class WidgetExample : IReady
{
    public int WidgetCount { get; set; }
    public int WidgetTarget { get; set; }
    public bool WidgetsReady { get; set; }

    public WidgetExample()
    {
        WidgetCount = 3;
        WidgetTarget = 45;
    }

    public bool ComputeReadiness()
    {
        if (WidgetCount < WidgetTarget)
        {
            WidgetsReady = false;
        }
        return WidgetsReady;
    }
}


public class Foo : WidgetExample
{
    public Foo()
    {
        this.WidgetTarget = 2;
    }
}

public class Bar : IReady
{
    public bool ComputeReadiness()
    {
        return true;
    }
}

解耦

public class UsesIReady
{
    public bool Start { get; set; }
    public List<string> WidgetNames { get; set; }

    //Here is the decoupling. Note that any object passed
    //in with type IReady will be accepted in this method
    public void BeginWork(IReady readiness)
    {
        if (readiness.ComputeReadiness())
        {
            Start = true;
            Work();
        }
    }

    private void Work()
    {
        foreach( var name in WidgetNames )
        {
            //todo: build name
        }
    }
}

多態性

public class Main
{
    public Main()
    {
        //Notice that either one of these implementations 
        //is accepted by BeginWork

        //Foo uses the abstract class
        IReady example = new Foo();
        UsesIReady workExample = new UsesIReady();
        workExample.BeginWork(example);

        //Bar uses the interface
        IReady sample = new Bar();
        UsesIReady workSample = new UsesIReady();
        workSample.BeginWork(sample);
    }
}

我一直在尋找答案,這些問題似乎都有些復雜。 所以這是我(希望)更簡單的答案。

  • 沒有任何實現細節可用於當前代碼范圍時,應使用接口
  • 當您可以使用某些實施細節時,應使用摘要
  • 而且,為了完整性,當所有實現細節都可用時,您應該使用

在解耦方面,雖然我有點同意Shelakel,為了這個問題的目的,並說明完全脫鈎的設計實踐,我建議如下:

  • 始終使用接口來定義外部行為。
  • 當您有一些可用的實現細節時 ,使用抽象類來定義它們,但是在抽象類上實現接口 ,並依次從這些類繼承。

這確保了以后如果您需要在新實現中更改一些模糊的實現細節,您可以在不修改現有抽象類的情況下執行此操作,並且還能夠將不同的實現類型分組到不同的抽象類中。

編輯:我忘了包含鏈接:) http://www.codeproject.com/Articles/11155/Abstract-Class-versus-Interface

抽象類和接口不是完全獨立的選擇。 我經常定義一個接口和一個實現該接口的抽象類。

接口確保最大的解耦,因為它不會強制您的類屬於特定的繼承層次結構,因此您的類可以繼承任何其他類。 換句話說,任何類都可以從接口繼承,而已經從其他類繼承的類不能從接口類繼承。

另一方面,在抽象類中,您可以分解出所有實現共有的代碼,而使用Interfaces,您必須從頭開始實現所有內容。 總而言之,通常最好的解決方案是使用BOTH一個抽象類和一個接口,因此如果可能的話,可以從重新使用抽象類中包含的公共代碼轉移到從頭開始重新實現接口,如果需要的話。

為了解耦而去耦是徒勞的。

接口用於集成,其中不需要知道具體細節(例如SendEmail())。 常見用途包括組件,服務,存儲庫以及IOC和通用實現的標記。

包含接口的泛型類型約束的擴展方法允許類似於Scala中具有類似可組合性的特征的功能。

public interface IHasQuantity { double Quantity { get; } }
public interface IHasPrice { decimal PricePerUnit { get; } }

public static class TraitExtensions
{
    public static decimal CalculateTotalPrice<T>(this T instance)
        where T : class, IHasPrice, IHasQuantity
    {
        return (decimal)instance.Quantity * instance.PricePerQuantity;
    }
}

在我看來,抽象類和類繼承被過度使用。

SOLID設計原則告訴我們,Liskov的替換原則意味着只有在繼承的類可以替代祖先的情況下才應該使用類繼承。 這意味着應該實現所有方法(不拋出新的NotImplementedExeption())並且應該按預期運行。

我個人已經發現類繼承在Template Method模式和狀態機的情況下都很有用。 在大多數情況下,構建模式等設計模式比深層繼承鏈更有用。

現在回到你的問題; 如果不是所有時間都應該使用大多數接口。 類繼承應該在內部使用,並且只能在外部用於定義,之后應該使用接口進行交互,並通過工廠提供具體實現或通過IOC容器注入。

理想情況下,在使用外部庫時,應創建接口並實現適配器以僅公開所需的功能。 大多數這些組件允許預先配置或在運行時通過IOC容器進行解析。

在解耦方面,將應用程序與其實現(尤其是外部依賴關系)分離以最小化更改原因非常重要。

我希望我的解釋能指出你正確的方向。 請記住,優先重構工作實現,然后定義接口以公開功能。

我不打算討論這兩種結構的優缺點,因為有足夠的資源。

但是,就一個組件與另一個組件“解耦”而言,接口繼承比抽象類或一般的類繼承要好得多(實際上我不認為抽象或不抽象在解耦方面沒有太大的區別,因為所有abstract does是在沒有具體實現的情況下阻止實例化的類)。

上述論點的原因是,接口允許您將暴露范圍縮小到“依賴組件”所需的絕對最小值,如果它需要單個方法接口可以輕松地做到這一點,或者甚至是沒有任何方法的標記接口。 對於基類(抽象或具體),這可能很難,因為它應該為該基礎實現所有“通用”功能。 因此,依賴於“基本類型”的組件將自動“看到”所有常見功能,即使它不需要它們用於它的目的。

接口還為您提供了最大的靈活性,因為即使是從沒有任何共同點的基礎繼承的類,仍然可以實現接口,並且可以被期望該接口的組件使用。 這方面的好例子是IDisposable接口。

所以,我的結論是解耦關注所有組件都依賴於接口而不是基類型,如果你發現實現該接口的大多數類都有一個共同的實現,那么就有一個實現該接口的基類並從該基類繼承其他類。

核心區別在於:

  • 接口公開零個或多個方法簽名 ,所有后代必須依次實現(否則代碼甚至不會編譯)。 接口公開的方法可以隱式實現(從接口派生的每個類型都可以訪問它們)或顯式實現(只有在將對象類型化為接口類型本身時才能訪問方法)。 在這個問題中可以找到更多細節和示例

  • 抽象類暴露零個或多個完整的方法,后代可以使用或覆蓋,提供自己的實現。 此方法允許您定義可自定義的“默認”行為。 抽象類允許您輕松添加沒有問題的新方法(在向抽象類添加方法時, NotImplementedException真的很閃亮),而向接口添加方法則需要修改實現它的所有類。

最后一點是,一個類可以同時實現多個接口。 一些現實世界的例子可能是:

  • 提供USB和LAN端口的硬盤驅動器是多接口繼承的良好示范
  • 筆記本電腦上有一個標有“藍牙”但沒有藍牙硬件的筆記本電腦很好地類比了沒有實現抽象方法的概念(你有LED,你有小B符號,但屋頂下沒什么)。

編輯1

這是一個MSDN鏈接,解釋了如何在接口和類之間進行選擇。

使用抽象類定義合同意味着您的實現者必須從此抽象類繼承。 由於C#不支持多重繼承,因此這些實現者將無法擁有備用類層次結構,這對某些人來說可能非常有限。 換句話說,抽象類基本上會破壞類層次結構功能的實現者,這通常需要獲取或使用某些其他功能(框架或類庫)。

使用接口定義合同會使類層次結構免費供您的實現者使用他們認為合適的任何方式,換句話說,提供更多的實現自由。

從評估標准的角度來看,當我們討論耦合時,我們可以談到三個可分離作者的關注點,客戶使用(調用)API /合同,API /合同的定義者以及API /合同的實現者; 我們可以談論自由(限制越少越好),封裝(必要的意識越少越好),以及面對變化時的彈性。

我認為接口導致比抽象類更松散的耦合,特別是在定義者和實現者之間,因為實現者提供了更高的自由度。

另一方面,當涉及到版本控制時,您至少可以向抽象類添加另一個方法,而不必更新子類實現,前提是添加的方法在抽象類中有實現。 跨DLL邊界的版本控制接口通常意味着添加另一個接口,推出起來要復雜得多。 (當然,如果您可以將所有實現重構在一起(例如,因為它們都在同一個DLL中),這不是問題。)

理解和記住接口抽象類之間差異的最好方法是,要記住抽象類是一個普通類 ,你可以用普通類抽象類 ,但有兩個例外

  1. 無法實例化抽象類
  2. 只能在抽象類中使用抽象方法

編碼到接口提供了可重用性和多態性。就類實現接口而言,接口或抽象類可以傳遞給參數而不是實現接口的類。在設計接口和抽象類中處理常見的技術問題並實現它並給出子類化特定的功能實現。想象它就像framework.Framework定義接口和抽象類並實現它對所有人都是通用的。那些抽象的是客戶端根據自己的要求實現的。

public interface Polymorphism{
void run();
Void Bark();
Energy getEnergy(Polymorphism test);
Public abstract class EnergySynthesis implements Polymorphism{
abstract void Energy();
Void Bark(){
 getEnergy(){

}
void run(){
getEnergy();

}public EnegyGeneartion extends EnergySynthesis  {
Energy getEnergy(Polymorphism test){
return new Energy( test); 
}
MainClass{

EnegyGeneartion test=new EnegyGeneartion ();
test.getEnergy(test);
test.Bark()
.
.
.
.
.
//here in Energy getEnergy(Polymorphism test) any class can be passed as parameter that implemets interface 

暫無
暫無

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

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