1. 程式人生 > >C#基礎系列——一場風花雪月的邂逅:介面和抽象類

C#基礎系列——一場風花雪月的邂逅:介面和抽象類

前言:最近一個認識的朋友準備轉行做程式設計,看他自己邊看視訊邊學習,挺有幹勁的。那天他問我介面和抽象類這兩個東西,他說,既然它們如此相像, 我用抽象類就能解決的問題,又整個接口出來幹嘛,這不是誤導初學者嗎。博主呵呵一笑,回想當初的自己,不也有此種疑惑麼。。。今天打算針對他的問題,結合一個實際的使用場景來說明下抽象類和介面的異同,到底哪些情況需要用介面?又有哪些情況需要用抽象類呢?

C#基礎系列目錄:

一、業務場景介紹。

博主打算使用原來在華為做外包的時候一個場景:我們針對華為裡面的裝置做了一個採集裝置使用率的程式,裝置的型別很多,各種裝置的登入和登出方式基本相同,但是每種裝置的採集的規則又不太相同。大致的場景就這樣,我們來看程式碼吧。

二、程式碼示例

根據業務場景,我們簡單搭建程式碼,先來看看程式碼結構圖:

ESTM.Spider:專案的入口程式,只為測試,這裡就簡單用了一個控制檯程式。

ESTM.Spider.Huawei:華為裝置採集規則,定義介面抽象實現和具體實現。

ESTM.Utility:解決方案的工具類和介面。

下面來看看具體的實現程式碼:

1、工具類

namespace ESTM.Utility
{
    public class LoginUser
    {
        public string Username { set; get; }

        public
string Password { set; get; } } public class Device { public string DeviceType { set; get; } public int WaitSecond { set; get; } } }

2、介面設計:ISpider.cs

namespace ESTM.Utility
{
    //採集介面,定義採集的規則
    public interface ISpider
    {
        bool Login(LoginUser oLoginUser);

        
string Spider(Device oDevice); void LoginOut(); } }

3、介面抽象實現類:SpiderBase.cs

   /// <summary>
    /// 公共的採集基類
    /// </summary>
    public abstract class SpiderBase : ISpider
    {
        //華為裝置統一採用Telnet方式登入。統一使用者名稱密碼都是admin。
        public virtual bool Login(LoginUser oLoginUser)
        {
            Console.WriteLine("華為裝置採用Telnet方式登入。");

            var bRes = false;
            if (oLoginUser.Username == "admin" && oLoginUser.Password == "admin")
            {
                Console.WriteLine("使用者名稱密碼校驗正確,登入成功");
                bRes = true;
            }
            else
            {
                Console.WriteLine("使用者名稱密碼校驗錯誤,登入失敗");
            }
            return bRes;
           
        }


        //採集操作和具體的裝置型別相關,這裡用抽象方法,要求子類必須重寫
        public abstract string Spider(Device oDevice);
        

        //華為裝置統一登出
        public virtual void LoginOut()
        {
            Console.WriteLine("華為裝置採用Telnet方式登出");
        }
    }

4、介面具體實現類

  [Export("MML", typeof(ISpider))]
    public class SpiderMML:SpiderBase
    {
        //MML裝置採集
        public override string Spider(Device oDevice)
        {
            Console.WriteLine("MML裝置開始採集");
            return "MML";
        }
    }
    [Export("TL2", typeof(ISpider))]
    public class SpiderTL2:SpiderBase
    {
        //TL2裝置採集
        public override string Spider(Device oDevice)
        {
            Console.WriteLine("TL2裝置開始採集");
            return "TL2";
        }
    }

5、在控制檯呼叫

  class Program
    {
        [Import("MML", typeof(ISpider))]
        public ISpider spider { set; get; }

        static void Main(string[] args)
        {
            var oProgram = new Program();
            RegisterMEF(oProgram);

            oProgram.spider.Login(new LoginUser() { Username = "admin", Password = "admin" });
            oProgram.spider.Spider(new Device() { DeviceType = "HuaweiDevice", WaitSecond = 100 });
            oProgram.spider.LoginOut();
        }

        #region 註冊MEF
        private static void RegisterMEF(object obj)
        {
            AggregateCatalog aggregateCatalog = new AggregateCatalog();
            var thisAssembly = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
            aggregateCatalog.Catalogs.Add(thisAssembly);
            var _container = new CompositionContainer(aggregateCatalog, true);
            _container.ComposeParts(obj);
        } 
        #endregion
    }

6、說明

這是一種比較典型的應用場景。介面定義規則,抽象類定義公共實現或者抽象方法,具體子類實現或者重寫抽象類方法。我們重點來看這裡的中間橋樑——抽象類。我們知道,抽象類裡面既可以有實現的方法,也可以有未實現的抽象方法。

(1)在這裡,Login、LoginOut方法由於子類是通用的具有相同邏輯的方法,所以我們需要在抽象類裡面去實現這兩個方法,如果子類沒有特殊需求,呼叫的時候直接用父類的方法就好了; 如果子類有特殊需求,可以override父類的方法。這樣設計既提高了程式碼的複用率,也可以靈活複寫。

(2)另一方面,抽象類裡面也定義了抽象方法,這個抽象方法在這裡的作用就很好體現了:如果子類不重寫父類的抽象方法,編譯通不過,直接報錯。這樣就要求我們子類必須要重寫抽象方法。從這點來說,抽象方法和介面的方法申明區別不大。

(3)如果這裡不用抽象類,就用一個普通的類來代替行不行?博主的答案是:行!但不好!如果你非要說,我用一個普通的類,將public abstract string Spider(Device oDevice);這個方法寫成

public virtual string Spider(Device oDevice)
{
      return "";  
}

貌似也沒問題,反正子類要重寫的。確實,這樣設計沒問題,但是如果你不慎子類忘了override呢?程式還是會跑起來,執行的時候可能會報錯。微軟既然給我們提供了abstract這麼一個東西,我們為什麼不用呢。

三、程式碼擴充套件

以上我們抽象類使用的必要性和使用方法是介紹完了。那麼接下來新的問題來了,可能就有人問了,你上面說了叭叭叭說了這麼多,無非就是說了抽象類的必要性,那麼既然抽象類這麼有用,我們直接用抽象類就好了,你幹嘛還要弄一個介面呢。談到這裡,就要說到面向介面程式設計。其實,面向介面程式設計和麵向物件程式設計並不是平級的,它並不是比面向物件程式設計更先進的一種獨立的程式設計思想,而是附屬於面向物件思想體系,屬於其一部分。或者說,它是面向物件程式設計體系中的思想精髓之一。而之前博主的文章就分享過面向介面程式設計的意義所在:依賴倒置,鬆耦合。那麼這裡是否可以不要介面,直接用抽象類代替呢?答案還是行!但不好!

比如我們現在又來了新的需求,中興也要用我們的採集系統,但是它的裝置型別、登入登出方式和華為裝置區別非常大。那麼這個時候我們介面的意義就體現了,如果我們使用介面,我們只需要再重寫一個ESTM.Spider.Huawei這個專案就好了,我們暫且命名叫ESTM.Spider.Zhongxing。我們來看看:

程式碼如下:

namespace ESTM.Spider.Zhongxing
{
    /// <summary>
    /// 中興裝置採集基類
    /// </summary>
    public abstract class SpiderBase:ISpider
    {
        //中興裝置通用登入方法
        public virtual bool Login(LoginUser oLoginUser)
        {
            Console.WriteLine("中興裝置登入前多了一個數據校驗:.......");
            Console.WriteLine("中興裝置採用WMI方式登入。");

            var bRes = false;
            if (oLoginUser.Username == "root" && oLoginUser.Password == "root")
            {
                Console.WriteLine("使用者名稱密碼校驗正確,登入成功");
                bRes = true;
            }
            else
            {
                Console.WriteLine("使用者名稱密碼校驗錯誤,登入失敗");
            }
            return bRes;
        }

        //定義抽象方法,要求子類必須重寫
        public abstract string Spider(Device oDevice);

        //中興裝置通用登出
        public virtual void LoginOut()
        {
            Console.WriteLine("中興裝置採用WMI方式登出");
        }
    }
}
namespace ESTM.Spider.Zhongxing
{
    [Export("ZXGC", typeof(ISpider))]
    public class SpiderZXGC:SpiderBase
    {
        public override string Spider(Utility.Device oDevice)
        {
            Console.WriteLine("中興ZXGC裝置開始採集");
            return "ZXGC";
        }
    }
}
namespace ESTM.Spider.Zhongxing
{
    [Export("ZXGY", typeof(ISpider))]
    public class SpiderZXGY:SpiderBase
    {
        public override string Spider(Utility.Device oDevice)
        {
            Console.WriteLine("中興ZXGY裝置開始採集");
            return "ZXGY";
        }
    }
}

由於這裡採用了介面,我們將ESTM.Spider.Zhongxing這個專案開發完成後生成dll,將dll放到控制檯程式中,直接通過MEF匯入不同的子類物件就可以使用,不需要更改控制檯裡面的大部分東西。如果不用介面,而是直接用抽象類代替,那麼控制檯裡面大部分的程式碼都得改,並且控制檯程式依賴多個dll,對設計的鬆耦合也不利。博主這裡為了簡單,用了MEF來簡單匯入,其實正式專案中,應該是用工廠採用反射直接創建出具體的例項。

四、總結

1、介面是一組規則的集合,它主要定義的是事物的規則,體現了是這種型別,你就必須有這些規則的概念。它的目的主要是依賴倒置和鬆耦合,從這點來說,介面不能省掉或者用抽象類代替。總而言之,介面和抽象類不可同日而語。

2、抽象類主要用於公共實現和約束子類必須重寫。以上面的例子說明,Login、Loginout用於公共實現,提高了程式碼複用,Spider用於抽象,約束子類必須要重寫Spider方法。這也就是這裡不能用普通類的原因。

3、用一句話概括介面和抽象類的區別:使用抽象類是為了程式碼的複用,而使用介面的動機是為了實現多型性(依賴倒置)。至於使用的時候到底是用介面還是抽象類,看具體的情況。

相關推薦

C#基礎系列——風花雪月邂逅介面抽象

前言:最近一個認識的朋友準備轉行做程式設計,看他自己邊看視訊邊學習,挺有幹勁的。那天他問我介面和抽象類這兩個東西,他說,既然它們如此相像, 我用抽象類就能解決的問題,又整個接口出來幹嘛,這不是誤導初學者嗎。博主呵呵一笑,回想當初的自己,不也有此種疑惑麼。。。今天打算針對他的問題,結合一個實際的使用場景來說明下

JAVA介面抽象的區別

1.從設計層面來說,抽象是對類的抽象,是一種模板設計 ,介面是行為的抽象,是一種行為的規範 介面 interface 抽象類 介面中所有方法都是抽象的 抽象類中可以有抽象方法和普通方法 一

JAVA面試題介面抽象的區別聯絡

一、介面的概念: 介面(Interface),在JAVA程式語言中是一個抽象型別,是抽象方法的集合。介面通常以interface來宣告。一個類通過繼承介面的方式,從而來繼承介面的抽象方法。 如果一個類只由抽象方法和全域性常量組成,那麼這種情況下不會將其定義為一個抽象類。只

設計模式(04)介面抽象

# 介面和抽象類 ## 區別 介面是對行為的抽象,其重點關注的是要有該行為。 抽象類是對一些共性行為的聚合,將多個子類都有的具體行為抽象成一個方法,形成複用。 ```java public abstract class Bird { private String name; privat

Java基礎深入理解Java的介面抽象

     對於面向物件程式設計來說,抽象是它的一大特徵之一。在Java中,可以通過兩種形式來體現OOP的抽象:介面和抽象類。這兩者有太多相似的地方,又有太多不同的地方。很多人在初學的時候會以為它們可以隨意互換使用,但是實際則不然。今天我們就一起來學習一下Java中的介面和

Python基礎----繼承派生、組合、接口抽象

子類 tool study href 組合 name anim walk 年齡 類的繼承與派生 經典類和新式類 在python3中,所有類默認繼承object,但凡是繼承了object類的子類,以及該子類的子類,都稱為新式類(在python3中所有的類都是新式類) 沒有繼

第6章繼承抽象

繼承 新的類通過繼承可以獲得已有類的所有特性和行為 繼承允許兩個類(子類和超類)之間共享資料和方法 可以複用已有的程式碼,從而消除冗餘性 使得軟體系統的維護和驗證變得簡

c#介面抽象的區別

   大家都容易把這兩者搞混,我也一樣,在聽李建忠老師的設計模式時,他也老把抽象類說成介面,弄的我就更糊塗了,所以找了些網上的資料.       一、抽象類:     

C#中的介面抽象學習

今天學習了介面和抽象類,但並沒有很好的進行整理,所以現在寫的時候,腦子裡多少有點亂亂的,先從介面開始吧。 interface 介面,規定了所有派生類的需要遵循的標準,介面定義了需要做些什麼,但是沒有具體的做法,做法的實現由派生類來做。 介面的定義使用關鍵字Interface,由於需要派生類繼承,所以

java基礎知識1:關鍵字;介面抽象;java併發相關

true、false、null都不是關鍵字 goto、const、是保留的關鍵字 abstract continue for new switch defa

探究Java的介面抽象--------Java的系列學習之路(12)

前言—— 今天第二天軍訓,訓得有點累,但是訓完整個人感覺很舒服,之前太久沒運動讓整個人感覺起來都很沒有 活力~   注:歡迎轉載,轉載請註明來處 目錄 一.抽象類 一.抽象類 a.要了解抽象類之前,我們需要先知道什麼是抽象方法? 抽象方法就是方法前面加

、面試準備--介面抽象有什麼區別

對於面向物件程式設計來說,抽象是它的一大特徵之一。在Java中,可以通過兩種形式來體現OOP的抽象:介面和抽象類。 首先,看一下兩者的定義: 抽象類 抽象類是用來捕捉子類的通用特性的 。它不能被例項化,只能被用作子類的超類。抽象類是被用來建立繼承層級裡子類的模板。

Java基礎學習之介面抽象的概念、區別及適用的場景

1.介面   介面是抽象方法的集合,一個介面只有方法的形狀而沒有方法的具體實現,介面是Java面向物件提供的一種機制。   Java語言是一種單繼承的,在類的繼承中通過實現多個介面間接實現了多繼承功能。介面的宣告: [public] interface InterfaceName{

C#介面抽象:Interface、abstract

一、介面 介面是C#中很常見的工具,概念什麼的就不說了,這裡講幾個值得注意的小地方: 1、介面內部只能有函式、屬性和事件的宣告: <!--<br /> <br /> Code highlighting produced by Actip

知識點4介面抽象有何區別

從語法層面而言,介面和抽象類的區別如下: 1.抽象類可以提供成員方法的實現細節,而介面中只能存在抽象方法(預設 public abstract) 2.抽象類中的成員變數可以是多種型別,而介面中的成員

c++介面抽象

其實對抽象類與介面的區別一直是搞不太清楚,最近正在學習《設計模式》,期間用到了很多c++多型的知識。這是才恍然發現,是應該整理下這方面的知識了。但在翻閱書本、上網查閱資料之際,發現有篇文章總結的不錯。於是,轉載(博主勿怪)並稍作修改如下: 抽象類:抽象類是特殊的類,只是不能

介面抽象你應該知道的基礎

JAVA中的幾句箴言: 抽象類和介面中都可以包含靜態成員變數 抽象類中可以包含靜態方法,介面內不能包含靜態方法 abstract不能例項化,只能繼承,可以有抽象成員 抽象類是對類抽象,而介面是對行為的抽象。  介面:本身沒有任何實現,因為Java不涉及表象,而之描述public行為,所以Java介面

夯實基礎系列Java 基礎總結

前言 大學期間接觸 Java 的時間也不短了,不論學習還是實習,都讓我發覺基礎的重要性。網際網路發展太快了,各種框架各種技術更新迭代的速度非常快,可能你剛好掌握了一門技術的應用,它卻已經走在淘汰的邊緣了。 而學習新技術總要付出一定的時間成本,那麼怎麼降低時間成本呢?那就是打好基礎,技術再怎麼革新,底層的東西也

lua繫結C++物件系列——基礎知識

table: 0x1d5a930 //l_registry[2]的地址 select function: 0x425589 require function: 0x1d5cb60 rawget function: 0x424cd3 rawlen function: 0x424c62 dofile f

C#基礎系列Attribute特性使用

前言:總結了下反射得基礎用法,這章我們來看看C#的另一個基礎技術——特性。 1、什麼是特性:就博主的理解,特性就是在類的類名稱、屬性、方法等上面加一個標記,使這些類、屬性、方法等具有某些統一的特徵,從而達到某些特殊的需要。比如:方法的異常捕捉,你是否還在某些可能出現