interface與abstract之我見

  • 9033
  • 0
  • C#
  • 2020-10-29

筆者在第一個專案中大量了使用物件導向的三大特性之一 封裝,而繼承與多型究竟是甚麼?這篇目的是為了強迫自己開始學習去理解物件件導向其他兩大特性。這篇就針對interface與abstract查到的資料做整理與消化! 但因為真的很不熟悉,若有觀念上錯誤,歡迎各位先進糾正!

What is interface?


在解釋interface前,要先了解甚麼是繼承
在程式碼中,有些情況是可將部分程式碼抽離,再透過繼承的方法整合,使子層可以擁有與父層相同的特性,例如貓、狗、鳥都會叫、跑、吃東西,不同牌手機都有音量控制鈕、開關機、號碼輸入等類似功能,如果逐一寫程式的話,會長得像這樣

class cat
{
    public void barking() => Console.WriteLine("叫");
}

class dog
{
    public void barking() => Console.WriteLine("叫");
}


因為動物都會叫,所以可新建一個Animals的類別,再讓貓、狗等動物使用繼承,就可以擁有父層的特性,好處是只要更新Animals,所有有繼承的子類別也會跟著連動修改,使用繼承的程式碼會長的像這樣

class Animals
{
    public void barking() => Console.WriteLine("叫");
}

class cat: Animals{}
class dog: Animals{}

有繼承就會有禁止繼承,關鍵字是sealed。使用在類別中,可以防止該類別被繼承,使用在方法,可防止被覆寫,有興趣深入的就自行google囉!


甚麼是interface(介面)?

筆者在網路上找到個還蠻平易近人的說法(參1),介面可以擁有不同的使用方法,但前提是必須建立在一個標準的規格,例如插座,對應可使用的機器有吹風機、吸塵器、電鍋等,USB對應可使用的有隨身碟、光碟機、手機充電線等。透過介面訂製(標準規格),可以反過來限制後續使用者必須遵照標準規格,避免許多奇形怪狀的規格出現。在C#中,任何類別只要繼承了interface,就會主動要求介面實作,介面實作的簡單的說法就是,在interface中只能描述有哪些方法,但不能明確下程式碼指定要做甚麼事情,一但任何類別繼承了interface,就會要求在該類別內,明確定義出interface中所有出現方法該要做什麼,否則會報錯。這樣在多人開發時,就可以避免不同的命名習慣所造成的問題。介面實作的程式碼會長的像這樣

interface animals
{
    void barking();
}

class cat : Ianimals
{
    public void barking()
    {
        Console.WriteLine("喵喵喵")
    }
}

使用interface還有第二個好處,我們先來試試看直接用繼承類別會出現甚麼結果

class Animals
{
    public void barking() => Console.WriteLine("叫");
}

class cat : Animals
{
    public void barking() => Console.WriteLine("喵喵喵");
}

class Program
{
    static void Main(string[] args)
    {
        Animals obj = new cat();
        obj.barking();
        Console.ReadLine();
    }
}

//結果會是"叫"

因為父的權限會大於子的權限,繼承父層可以讓程式碼變乾淨,但卻無法在子層修改方法,如果今天我想把叫聲改成「汪」、「喵」之類的就會有問題了。所以interface的第二個好處,在interface中指定必須包含哪些方法,但因為不需要實作,所以可以在被繼承的類別中才填寫要執行的內容,這樣就可以寫出更有彈性的寫法,當然,透過關鍵字virtual也是可以實現同樣功能唷!即便如此,還是有些微小的差異性,在C#中類別不能多重繼承,但interface經筆者測試是OK的。如果用JAVA的觀念來看,實作interface根本也不算是繼承,在JAVA裡實作是使用implement,而繼承則是extent。貼心小提醒,通常interface的變數命名會使用「I」作為開頭唷。

interface的眉眉角角
在網路上找到JAVA中interface介紹(參2),在JAVA中,interface定義的方法權限預設為public,型別預設都是abstract,所以加不加public與abstract都沒差,但經筆者在C#環境中測試,會跳出訊息並要求C#8.0才提供此寫法,筆者並不清楚這中間的差異是甚麼,這裡先做個紀錄,日後有空再來玩看看。但感覺應該是public 或是 protected,因為是可以被其他類別繼承並使用的。

 

Interface與Abstract class差異


在查詢資料時一直出現「抽象類」,有的好像是指abstract,有的好像又同時代表abstract與interface,真的是看到霧煞煞的,故筆者在這裡就統一都用英文來敘述。
 

比較兩者定義方法的差別

abstract class Animals { 
    abstract void method1(); 
    abstract void method2(); 
    … 
} 

interface Animals { 
    void method1(); 
    void method2(); 
    … 
}


在abstract class方式中,需要針對方法加上abstract,這意味者在abstract class中,應該是允許出現其他型別的,再來看看參考3在abstract class中允許出現int,經筆者測試,在interface中則不允許加上欄位(僅測試int),但屬性與方法是可以的。除此之外,在abstract class中的方法可加上protected、public等字眼,也完全不會報錯,代表在abstract class中應該允許可以擁有內部使用的方法,而非全都abstract的方法,再測試一下,賓果!

abstract class BaseClass   
{
    protected int _x = 100;
    protected int _y = 150;
    public abstract void AbstractMethod();   
    private void Test(){ }
    public abstract int X    { get; }
    public abstract int Y    { get; }
}


在interface的方法實作,可直接覆蓋,在abstract class中要修改任何欄位、屬性、方法都必須加上override。

class DerivedClass : BaseClass
{
    public override void AbstractMethod()
    {
        _x++;
        _y++;
    }

    public override int X   
    {
        get
        {
            return _x + 10;
        }
    }

    public override int Y  
    {
        get
        {
            return _y + 10;
        }
    }
}


什麼時候使用interface? 什麼時候使用abstract?
若把焦點放在方法實作上,可能會感到有點迷思,因為兩者好像都可以被覆寫。筆者也找到一篇算淺顯易懂的文章(參4),該文作者以門為例,當我們在建立類別時,一般門可能會有開門跟關門,假如有天要增加一個新的功能,比如自動門、設定密碼,作法一可以直接加入abstract或interface中,但這樣會面臨到耦合度的問題。當要建立木門所有功能,明明木門不能設定密碼,但因為父層的interface或abstract有設定密碼的功能,導致木門也得到這個功能了,再把問題換成公司系統,不同職位的權限或可執行的功能一定不同,若出現這樣的狀況是非常不合理的事情。看完了他的例子應該會更有感覺,abstract class仍屬於class,故僅能單一繼承,而繼承interface則可以多重繼承,所以門為例,屬於名詞的共通特性,應該放在abstract,而屬於其他覆加的功能,則可以放在interface,透過interface可多重實現的特性,工程師就可以自行組合成各式各樣的門,那今天就介紹到這裡啦!

 

參考資料

  1. http://benyi.logdown.com/posts/2018/02/11/oop-what-is-interface
  2. https://openhome.cc/Gossip/JavaGossip-V1/InterfaceType.htm
  3. https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/abstract
  4. https://jjnnykimo.pixnet.net/blog/post/21585257