設計模式前奏–多態

設計模式前奏–多態

這篇文章我將講解多態,在上篇文章設計模式前奏–封裝繼承中我說過爲什麼要面向接口編程,而不是面向細節編程,在這篇文章中我將用示例來說明。

面向對象三大特性:封裝、繼承、多態。

首先說明重載和多態不是一個概念。
重載:名稱相同,參數類型和參數個數不同
多態:“同參數、同返回類型。一個接口,多種實現”。
另外在多態中還有一個函數調用方式叫覆蓋(Override),也有翻譯爲重寫,和剛纔上面提到重載不是一概念,這點務必重點區分,同時多態是判斷一個面向對象編程語言是否屬於高級語言的一個重要條件,有些面向對象編程語言就不支持多態。

重載(Overload),是指允許存在多個同名函數,而這些函數的簽名也叫參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。其實,重載的概念並不屬於“面向對象編程”,重載的實現是:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。

多態(Polymorphisn),按字面的意思就是“多種形狀”。多態性是允許你將父對象設置成爲和一個或更多的其子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是所有可以放父類對象的地方都可以用它的子類對象來代替,並且有着子類對象的特性,如屬性和方法。面向對象設計模式有一個核心原則:里氏代換原則,就是說的這個。

好吧下面我們來看代碼

這裏寫圖片描述

class PC
{
    private string name;

    public PC(string name)
    {
        //this.name = name;
        this.name = !string.IsNullOrEmpty(name) ? name : "unknown brand";
    }

    public virtual string Name { get { return name; } set { name = value; } }

    //public abstract void Describe()
    public virtual void Describe()
    {
        System.Console.WriteLine("PC: I am {0} personal computer", Name);
    }
}

class Dell : PC
{
    public Dell(string name) : base(name)
    {

    }

    public override void Describe()
    {
        System.Console.WriteLine("Dell: I am {0} personal computer", Name);
    }
}

class IBM : PC
{
    private string name;

    public override string Name { get { return name; } set { name = value; } }

    public IBM(string name) : base(name)
    {
        this.name = name;
    }

    public override void Describe()
    {
        System.Console.WriteLine("IBM: I am {0} personal computer", Name);
    }
}

class Program
{
    static void Main(string[] args)
    {
        System.Console.WriteLine("Polymorphism \n");

        PC pc = new PC("");
        pc.Describe();
        pc.Name = "xxPC";
        pc.Describe();

        Console.Write("\n");

        PC dell = new Dell("Dell");    //面向接口編程
        //Dell dell = new Dell("Dell");//面向細節編程
        dell.Describe();
        dell.Name = "OtherDell";
        dell.Describe();

        Console.Write("\n");

        PC ibm = new IBM("IBM");       //面向接口編程
        //IBM ibm = new IBM("IBM");    //面向細節編程
        ibm.Describe();
        ibm.Name = "OtherIBM";
        ibm.Describe();


    }
}

這裏寫圖片描述

面向細節編程

//PC dell = new Dell("Dell");
Dell dell = new Dell("Dell");

//PC ibm = new IBM("IBM");
IBM ibm = new IBM("IBM");

這裏寫圖片描述

面向接口編程

PC dell = new Dell("Dell");
//dell = new Dell("Dell");

PC ibm = new IBM("IBM");
//IBM ibm = new IBM("IBM");

這裏寫圖片描述

請注意對比這二幅圖,你會發現得到的結果是一樣的。也許有同學會說這有什麼意義,雖然實現的方式不一樣,但得到結果都一樣。恰恰問題的關鍵就在這裏,也許現在你能不理解,那沒關係,因爲後繼我的文章裏會有關於設計模式的講解,到時你就會清楚我現在這篇文章要詮釋的意義。

以下代碼我把各種修飾語句先去掉

class PC
{
    private string name;

    public PC(string name)
    {
        //this.name = name;
        this.name = !string.IsNullOrEmpty(name) ? name : "unknown brand";
    }

    public string Name { get { return name; } set { name = value; } }

    //public abstract void Describe()
    public void Describe()
    {
        System.Console.WriteLine("PC: I am {0} personal computer", Name);
    }
}

class Dell : PC
{
    public Dell(string name) : base(name)
    {

    }

    public void Describe()
    {
        System.Console.WriteLine("Dell: I am {0} personal computer", Name);
    }
}

class IBM : PC
{
    private string name;

    public string Name { get { return name; } set { name = value; } }

    public IBM(string name) : base(name)
    {
        this.name = name;
    }

    public void Describe()
    {
        System.Console.WriteLine("IBM: I am {0} personal computer", Name);
    }
}

class Program
{
    static void Main(string[] args)
    {
        System.Console.WriteLine("Polymorphism \n");

        //PC pc = new PC("");
        //pc.Describe();
        //pc.Name = "xxPC";
        //pc.Describe();

        //Console.Write("\n");

        //PC dell = new Dell("Dell");
        Dell dell = new Dell("Dell");
        dell.Describe();
        dell.Name = "OtherDell";
        dell.Describe();

        Console.Write("\n");

        //PC ibm = new IBM("IBM");
        IBM ibm = new IBM("IBM");
        ibm.Describe();
        ibm.Name = "OtherIBM";
        ibm.Describe();
    }
}

//PC dell = new Dell(“Dell”);//代碼定義
Dell dell = new Dell(“Dell”);//代碼定義

//PC ibm = new IBM(“IBM”);
IBM ibm = new IBM(“IBM”);
這裏寫圖片描述
運行後調用的是子類Dell和IBM

PC dell = new Dell(“Dell”);
//dell = new Dell(“Dell”);

PC ibm = new IBM(“IBM”);
//IBM ibm = new IBM(“IBM”);
這裏寫圖片描述
運行後調用的是父類PC

這次發現二幅圖終於有變化了吧

我們來看是修改了哪裏
這裏寫圖片描述

這裏寫圖片描述
提示要使用關鍵字new

加上關鍵字並運行
這裏寫圖片描述

//
//PC dell = new Dell(“Dell”);
Dell dell = new Dell(“Dell”);

//PC ibm = new IBM(“IBM”);
IBM ibm = new IBM(“IBM”);
//爲了和上面對比,這裏我使用橙色標記
這裏寫圖片描述
//運行後調用的是子類Dell和IBM

//
PC dell = new Dell(“Dell”);
//dell = new Dell(“Dell”);

PC ibm = new IBM(“IBM”);
//IBM ibm = new IBM(“IBM”);
//使用綠色標記
這裏寫圖片描述
運行後調用的是父類PC,和沒加new前是一樣的。

下面把關鍵字new換成override
這裏寫圖片描述
提示”Dell.Describe()”:繼承成員 PC.Describe()”未標記爲virtual、abstract或override”,無法進行重寫

把父類加上virtual
這裏寫圖片描述

//PC dell = new Dell(“Dell”);
Dell dell = new Dell(“Dell”);

//PC ibm = new IBM(“IBM”);
IBM ibm = new IBM(“IBM”);
使用紫色標記
這裏寫圖片描述
運行後調用的是子類Dell和IBM

PC dell = new Dell(“Dell”);
//dell = new Dell(“Dell”);

PC ibm = new IBM(“IBM”);
//IBM ibm = new IBM(“IBM”);
使用青色標記
這裏寫圖片描述
運行後調用的同樣是子類Dell和IBM

到這裏你應該發現好像又回到最初我說的方式去了,就是面向接口編程。也許你現在還是不太能理解我說的意思,不過,沒關係,因爲如果你接觸到工廠方法、抽象工廠、策略等一系列模式後你就明白我現在這個示例的意義所在。

關於上面提示”Dell.Describe()”:繼承成員 PC.Describe()”未標記爲virtual、abstract或override”,無法進行重寫,我現在只做了修改爲virtual的示例, 另外二種又是什麼情況呢?如果非要說明父類除了使用virual,以及另外的abstract或override這二種情況,那麼這個類的繼承關係會更加複雜化,這篇文章我們就只討論第一種情況。注意子類是new或override。

爲進一步說明問題我再加入HP、Sony、Lenovo這三個對象

    class Dell : PC
    {
        public Dell(string name) : base(name)
        {

        }

        public override void Describe()
        {
            System.Console.WriteLine("Dell: I am {0} personal computer", Name);
        }
    }

    class IBM : PC
    {
        private string name;

        public override string Name { get { return name; } set { name = value; } }

        public IBM(string name) : base(name)
        {
            this.name = name;
        }

        public override void Describe()
        {
            System.Console.WriteLine("IBM: I am {0} personal computer", Name);
        }
    }

    class HP : PC
    {
        private string name;

        public override string Name { get { return name; } set { name = value; } }

        public HP(string name) : base(name)
        {
            this.name = name;
        }

        public new void Describe()
        {
            System.Console.WriteLine("HP: I am {0} personal computer", Name);
        }
    }

    class Sony : PC
    {
        private string name;

        public new string Name { get { return name; } set { name = value; } }

        public Sony(string name) : base(name)
        {
            this.name = name;
        }

        public override void Describe()
        {
            System.Console.WriteLine("Sony: I am {0} personal computer", Name);
        }
    }

    class Lenovo : PC
    {
        private string name;

        public new string Name { get { return name; } set { name = value; } }

        public Lenovo(string name) : base(name)
        {
            this.name = name;
        }

        public new void Describe()
        {
            System.Console.WriteLine("Lenovo: I am {0} personal computer", Name);
        }
    }

這裏寫圖片描述

        Dell dell = new Dell("Dell"); 

        IBM ibm = new IBM("IBM");

        HP hp = new HP("HP"); 

        Sony sony = new Sony("Sony");

        Lenovo lenovo = new Lenovo("Lenovo");

注意藍色部分Sony,以及橙色部分HP(這裏的HP輸出是調用HP,請比對下圖)
Sony:I am Sony personal computer
Sony:I am OtherSony personal computer
注意這裏的HP我沒做特別的標記,因爲此時探討Sony就已經很複雜。
這裏寫圖片描述

        PC dell = new Dell("Dell");  

        PC ibm = new IBM("IBM");

        PC hp = new HP("HP");  

        PC sony = new Sony("Sony");  

        PC lenovo = new Dell("Lenovo");  

注意綠色部分Sony,以及橙色部分HP(這裏的HP輸出是調用PC,請比對上圖).
Sony:I am Sony personal computer
Sony:I am Sony personal computer
這裏寫圖片描述

對比這二次Sony的調用有什麼不同?
PC sony = new Sony(“Sony”);
Sony sony = new Sony(“Sony”);
注意子類(Sony class)裏面new和override的地方,和其他子類對比下。

再回到前面看看我最初的代碼,Dell和IBM這二個子類的差別,就是沒加入對比前
這裏寫圖片描述
爲什麼Dell只實現了一個Describe()方法,而IBM還多了一個Name{}屬性。現在你知道我加入HP、Sony、Lenove這裏對比的目的了吧。

通過示例可以看到子類IBM覆蓋(override也叫重寫)了父類PC中的Name{}屬性,而Dell則完全沒有去實現這個方法(Name{}屬性),但卻最終得到的結果卻都 一樣(這裏必須強調此時IBM的Name{}屬性的修飾符是override,而不是new。)。

可能有人會說這裏是繼承,但其實更多的是關於多態的特性,否則我也不會用這麼多的示例和圖片去說明問題,多態和繼承本就是相關聯的,沒有繼承關係就沒有多態,也就是說多態是建立在繼承的基礎上的,就好比繼承是建立在封裝上一樣的。先有封裝,然後纔有繼承,最後纔是多態。再次說明我這裏雖然引入繼承,但這裏的重點不是說繼承,而是講解多態的複雜性,這個你要分清楚,當然你也可以把多態簡單的理解爲只是在子類中使用new或者override。

最後把基類改爲抽象類

abstract class PC
{
    private string name;

    public PC(string name)
    {
        //this.name = name;
        this.name = !string.IsNullOrEmpty(name) ? name : "unknown brand";
    }

    public virtual string Name { get { return name; } set { name = value; } }

  //把Describe這個方法也改爲抽象的,如果你的需求不是很特別,至少我認爲在這個示例程序裏應該是這樣做
    public abstract void Describe();
    //public virtual void Describe()
    //{
    //    System.Console.WriteLine("PC: I am {0} personal computer", Name);
    //}
} 

最後把pc = new PC(“”)的代碼也註釋掉,因爲通常我們通常定義基類(或者父類)都是抽象的,就也是說不允許創建抽象類的實例。

            //PC pc = new PC("");
            //pc.Describe();
            //pc.Name = "xxPC";
            //pc.Describe();


        PC dell = new Dell("Dell");    //面向接口編程
        //Dell dell = new Dell("Dell");//面向細節編程

        PC ibm = new IBM("IBM");       //面向接口編程
        //IBM ibm = new IBM("IBM");    //面向細節編程

如果你還是沒有弄清楚多態,或許你可以去看看這部影片《剌客攝隱娘》,感覺多態就像這部影片一樣,有人說故事情節很吸引人環環相扣,有人說看不懂,甚至還有人看到一半就走了,哎,越扯越遠了,呵呵。

言歸正傳,多態–這個面向對象編程領域的核心概念,本身的內容就很博大精深,要幾句話說清楚實在是不太可能。

說了這麼多不知道你理解我要表達的意圖了嗎?晚安!

track http://blog.csdn.net/cadenzasolo/article/details/50540089

版權所有,轉載請註明文章出處 http://blog/csdn.net/cadenzasolo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章