【公告】網站目前停止所有的課程訂閱服務,原有學員權益不受影響,造成不便還請見諒,我們正在打造更多課程以及圖書,包含 Python 為主的課程主題,未來將會合併且擴充目前的課程內容,提供全新課程訂閱服務,感謝學員的支持。

繼承 Inheritance

繼承是物件導向理論最重要的一項機制,透過繼承現有的類別,新建立的類別不需撰寫任何程式碼即可直接擁有原類別的功能,另外在需要的時候,建構自已的專屬功能,提供應用程式發展的彈性與擴充能力。

繼承的關係中,原始類別稱為基礎類別或是父類別,繼承原始類別的新類別則是衍生類別或子類別,衍生類別繼承基礎類別的公開成員,然後根據需求改寫或是擴充基礎類別的功能,衍生類別本身並不包含基礎類別的相關程式碼。



類別繼承首先要將新建立的類別宣告為繼承特定類別的子類別,以下為繼承語法格式:

class B : A
{
    // class body …
}

B 為新建立的子類別, A 則是基礎類別, B 類別透過冒號 ( : ) 繼承基礎類別 A ,來看以下的程式碼。

class Program
{
    static void Main(string[] args)
    {
        B b = new B();
        b.ShowHelloMessage();
        b.ShowWelcomeMessage();
        Console.ReadKey();
    }
}
public class A
{
    public void ShowHelloMessage()
    {
        string message = Hello();
        Console.WriteLine(message);
    }
    private string Hello()
    {
        string message = "Hello";
        return message;
    }
}
public class B : A
{
    public void ShowWelcomeMessage()
    {
        Console.WriteLine("Welcome");
    }
}

類別 A 有一個 public 方法 ShowHelloMessage() 以及 private 方法 Hello() ,前者引用後者取得訊息字串將其輸出。

類別B則繼承A,其中只有一個public方法ShowWelcomeMessage()。

在 Program中,建立類別B的物件實體,分別引用類A的ShowHelloMessage()以及類別B的ShowWelcomeMessage(),由於B繼承A,因此可以同時引用A與B的public方法,因此輸出了Hello與Welcome兩個訊息字串。

現在嘗試引用 A類別的Hello() 方法:

string msg = b.Hello();

這會導致以下的錯誤訊息:

錯誤 CS0122 'A.Hello()' 由於其保護層級之故,所以無法存取。

因為在A類別中,Hello() 是 private ,B類別無法繼承這個方法,因此導致程式編譯錯誤。

從這個範例可以看到透過繼承的設計,新的類別繼承基礎類別的公開成員,不需為了相同的功能撰寫重複的程式碼,然後根據需求改寫或是擴充自己專屬的功能,衍生類別本身並不包含基礎類別的相關程式碼,如此一來很容易就可以在不影響原有類別的前提下,同時提供系統維護與調整的彈性。

繼承關係下的功能異動

繼承最大的好處在於不影響原有程式碼的前提之下,同時提供系統維護與調整的彈性,基礎類別本身的修改與調整可以直接反應至所有繼承的子類別,而子類別根據自身需求所作的擴充與修改則不會影響到基礎類別以及其他子類別。

public class MathMD
{
    public double Multiplication(double a, double b)
    {
        double m = a * b;
        return m;
    }
    public double Division(double a, double b)
    {
        double d = a / b;
        return d;
    }
}
public class MathArithmetic : MathMD
{
    public double Addition(double a, double b)
    {
        double av = a + b;
        return av;
    }
    public double Subtraction(double a, double b)
    {
        double s = a - b;
        return s;
    }
}

類別MathMD定義了Multiplication方法,支援兩個數值的乘法運算,另外Division則支援除法運算,MathArithmetic類別除了繼承類別MathMD的方法,另外定義加法運算方法Addition以及減法運算subtraction,因此擁有完整的四則運算方法,以下的程式碼嘗試建立MathArithmetic物件,並引用繼承至類別MathMD的Division方法,執行100與20的除法運算。

MathArithmetic ma = new MathArithmetic();
double d = ma.Division(100, 20);
Console.WriteLine(d);

這段程式碼輸出 20 的結果。考慮以下另下的程式碼:

double d = ma.Division(100, 0);

這會得到無限的結果,我們希望Division()方法可以排除0的參數,調整程式碼如下:

public double Division(double a, double b)
{
    if (b == 0)
    {
        Console.WriteLine("除數不得為零 !!");
        return 0;
    }
    else
    {
        double d = a / b;
        return d;
    }
}

其中判斷參數b,如果是0則輸出說明訊息,並且回傳0。
現在重新透過MathArithmetic物件ma進行引用,並且傳入0,得到以下的輸出結果:

除數不得為零 !!
0

繼承架構在大規模的程式設計特別有用,無論基礎類別底下繼承多少數目的子類別,都可以經由繼承關係將程式異動的部份直接向下傳遞,想像一個包含幾十個甚至數百個子類別的系統,程式的問題經過基礎類別的修正之後,所有繼承架構底下的子類別都可以直接享受到程式更新的好處。

繼承關系只會向下傳遞,子類別本身的調整並不會影響其繼承的基礎類別,當一個已經上線運作的系統需要更新功能或是有任何問題需要重新作修正,只需針對特定類別直接進行處理即可,不會影響到其他非繼承架構裏的類別物件。



在上述的範例中,如果只是調整MathArithmetic類別中的任何程式碼,都不會影響其繼承的MathMD類別。




沒有留言: