Day 13 我們學到 Inheritance。
現在知道:
Animal animal = new Dog();
是成立的。
因為:
Dog is an Animal
所以:
Variable Type
→ Animal
實際 Object Type
→ Dog
但新的問題來了。
Dog
→ 汪
Cat
→ 喵
Dog 和 Cat 都是 Animal,但它們的行為不同。
如果 Variable Type 都是 Animal,程式怎麼知道該執行 Dog 還是 Cat 的行為?
這就是今天要解決的問題。
今天會學到:
virtual
override
abstract
先建立:
class Animal
{
public void MakeSound()
{
Console.WriteLine("Animal Sound");
}
}
接著:
class Dog : Animal
{
}
class Cat : Animal
{
}
因為:
Dog is an Animal
Cat is an Animal
所以它們都有:
MakeSound()
但實際上:
Dog
→ 汪
Cat
→ 喵
也就是:
雖然 Dog 和 Cat 都是 Animal,但它們對同一個行為有不同的實作方式。
接下來要解決:
Derived Class 要怎麼提供自己的行為?
virtual 與 override先修改 Animal:
class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal Sound");
}
}
這裡:
virtual
代表:
Base Class 提供預設實作,同時允許 Derived Class 改寫。
接著 Dog:
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("汪");
}
}
Cat:
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("喵");
}
}
這裡:
override
代表:
Derived Class 提供自己的版本。
可以先記:
virtual
→ Base Class 允許改寫
override
→ Derived Class 提供自己的實作
例如:
Dog dog = new Dog();
dog.MakeSound();
結果:
汪
到這裡我們知道:
Dog
→ 有自己的 MakeSound()
Cat
→ 有自己的 MakeSound()
但真正重要的問題是:
如果 Variable Type 是 Animal,還會執行 Dog 的 MakeSound() 嗎?
假設:
Animal animal = new Dog();
這裡:
Variable Type
→ Animal
實際 Object Type
→ Dog
現在呼叫:
animal.MakeSound();
結果:
汪
而不是:
Animal Sound
為什麼?
因為 MakeSound() 使用了:
virtual
+
override
當程式執行時,會根據:
實際 Object Type
決定要執行哪個版本。
所以:
animal.MakeSound()
↓
實際 Object Type 是 Dog
↓
Dog.MakeSound()
↓
汪
這就是理解 Polymorphism 的關鍵。
再看:
Animal animal1 = new Dog();
Animal animal2 = new Cat();
兩個 Variable Type 都是:
Animal
但實際 Object Type 不同:
animal1
→ Dog
animal2
→ Cat
現在呼叫相同 Method:
animal1.MakeSound();
animal2.MakeSound();
結果:
汪
喵
可以整理成:
共同 Base Type
→ Animal
相同 Method
→ MakeSound()
不同 Object
→ Dog / Cat
不同實際行為
→ 汪 / 喵
這就是:
Polymorphism(多型)
先白話理解:
使用相同的 Base Type 呼叫相同 Method,不同 Object 可以執行自己的行為。
也就是:
相同 Base Type
↓
相同 Method
↓
不同 Object
↓
不同實際行為
這兩個概念有關,但解決不同問題。
override
→ Derived Class 怎麼提供自己的行為?
Polymorphism
→ 呼叫端怎麼透過共同 Type
操作不同 Object?
例如:
Animal animal = new Dog();
animal.MakeSound();
可以拆成:
Dog.MakeSound()
→ override
Animal animal
→ 透過共同 Type 操作 Dog
執行 Dog.MakeSound()
→ Polymorphism
簡單理解:
Override 負責提供不同實作;Polymorphism 負責在執行時使用正確的實作。
如果呼叫端自己判斷每一種 Type,可能會寫:
if (animal is Dog)
{
Console.WriteLine("汪");
}
else if (animal is Cat)
{
Console.WriteLine("喵");
}
如果未來增加:
Cow
Bird
Lion
判斷可能越來越多:
if Dog
else if Cat
else if Cow
else if Bird
else if Lion
...
但使用 Polymorphism:
animal.MakeSound();
就可以了。
因為:
呼叫端
→ 只負責呼叫 MakeSound()
實際 Object
→ 決定 MakeSound() 怎麼執行
所以:
呼叫端只依賴共同的 Base Type,不需要一直判斷實際是哪一個 Derived Type。
List<Animal> 更明顯List<Animal> animals = new List<Animal>
{
new Dog(),
new Cat()
};
接著:
foreach (Animal animal in animals)
{
animal.MakeSound();
}
結果:
汪
喵
foreach 裡完全不需要:
if (animal is Dog)
也不需要:
if (animal is Cat)
只有:
animal.MakeSound();
因為:
呼叫端
→ 決定要做什麼
→ MakeSound()
實際 Object
→ 決定怎麼做
→ 汪 / 喵
這就是 Polymorphism 的價值。
前面使用:
class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal Sound");
}
}
這代表:
Animal
→ 已經提供 MakeSound() 的預設版本
Dog / Cat
→ 如果需要,可以 override
但仔細想:
Animal 本身真的有一個合理的預設叫聲嗎?
其實沒有。
因為:
Dog
→ 汪
Cat
→ 喵
Cow
→ 哞
所以真正想表達的不是:
Animal
→ 我先提供一個預設叫聲
而是:
所有 Animal
→ 都必須有 MakeSound()
但是
Animal
→ 不知道 MakeSound() 應該怎麼實作
Dog / Cat
→ 各自決定怎麼 MakeSound()
換句話說:
Base Class 想規定「一定要有這個行為」,但自己沒有合理的預設實作。
這種情況就適合使用:
abstract
abstract Method:規定一定要有,但 Base 不實作把 Animal 改成:
abstract class Animal
{
public abstract void MakeSound();
}
先看:
public abstract void MakeSound();
它只有 Method 宣告:
MakeSound()
沒有 Method Body。
也就是:
Animal 只規定「一定要有 MakeSound()」,但不規定要怎麼做。
因此:
Animal
→ 定義規則
→ 每一種 Animal 都必須有 MakeSound()
Dog
→ 決定 Dog 的 MakeSound() 怎麼做
Cat
→ 決定 Cat 的 MakeSound() 怎麼做
Dog:
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("汪");
}
}
Cat:
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("喵");
}
}
可以先記:
abstract
→ Base Class 提出要求
→ 自己不提供實作
override
→ Derived Class 回應要求
→ 提供自己的實作
如果 Derived Class 本身不是 Abstract Class,就必須實作繼承到的 Abstract Member。
例如下面這樣不行:
class Dog : Animal
{
}
因為 Animal 已經規定:
Dog
→ 必須提供 MakeSound()
所以必須:
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("汪");
}
}
剛才:
abstract class Animal
{
public abstract void MakeSound();
}
這裡其實有兩個概念:
abstract class Animal
→ Animal 是 Abstract Class
public abstract void MakeSound()
→ MakeSound() 是 Abstract Method
兩者不要混在一起。
代表:
這個 Method 沒有實作,Derived Class 必須提供實作。
例如:
public abstract void MakeSound();
代表:
這個 Class 用來定義共同概念與規則,但不能直接建立 Object。
所以不能:
Animal animal = new Animal();
因為:
Animal
→ Abstract Class
→ 不能直接 new
但可以:
Animal animal = new Dog();
因為真正建立的是:
Dog Object
而 Animal 作為共同的 Base Type:
Variable Type
→ Animal
實際 Object Type
→ Dog
abstractAbstract Class 仍然可以包含:
Property
Constructor
一般 Method
virtual Method
abstract Method
例如:
abstract class Animal
{
public string Name { get; private set; }
public Animal(string name)
{
Name = name;
}
public void Eat()
{
Console.WriteLine("Eating");
}
public abstract void MakeSound();
}
這裡:
Name
→ 共同資料
Eat()
→ Animal 已經知道怎麼做
→ 提供共同實作
MakeSound()
→ Animal 不知道怎麼做
→ Derived Class 自己實作
所以:
Abstract Class
├── 可以放共同資料
├── 可以放已經實作好的共同 Method
└── 可以規定 Derived 必須實作的行為
virtual、abstract、override 差在哪裡?這裡不用死背三個 Keyword。
先站在:
Base Class
的角度問:
Base Class 自己有沒有合理的實作?
例如:
public virtual void Move()
{
Console.WriteLine("Animal is moving");
}
Animal 已經知道:
Move()
→ 預設可以怎麼做
所以可以直接提供實作。
Derived Class 可以直接沿用。
如果需要不同版本,再使用:
override
例如:
class Bird : Animal
{
public override void Move()
{
Console.WriteLine("Bird is flying");
}
}
所以:
virtual
→ Base 已經有預設版本
→ Derived 可以直接沿用
→ 也可以 override
可以把 virtual 想成:
「我有一個預設版本,你想改的話可以改。」
例如:
public abstract void MakeSound();
Animal 知道:
所有 Animal
→ 都應該 MakeSound()
但不知道:
MakeSound()
→ 到底應該怎麼做
所以 Base 不提供實作。
而是要求 Derived Class 自己完成。
abstract
→ Base 沒有預設版本
→ 只規定一定要有
→ Derived 必須提供實作
可以把 abstract 想成:
「我沒有預設版本,但你一定要自己完成。」
override 呢?override 是 Derived Class 使用的。
它代表:
「我要提供自己的版本。」
例如:
public override void MakeSound()
{
Console.WriteLine("汪");
}
所以三者角色不同:
virtual
→ Base Class
→ 有預設實作
→ Derived 可以改
abstract
→ Base Class
→ 沒有預設實作
→ Derived 必須實作
override
→ Derived Class
→ 提供自己的實作
Base Class 有合理的預設實作嗎?
│
┌───────┴───────┐
│ │
Yes No
│ │
virtual abstract
│ │
↓ ↓
Derived 可選擇 Derived 必須
是否 override 提供實作
| 比較 | virtual |
abstract |
|---|---|---|
| 誰宣告 | Base Class | Base Class |
| Base 有沒有實作 | 有 | 沒有 |
| Derived 能不能直接沿用 | 可以 | 不行 |
| Derived 是否一定要實作 | 不一定 | 是 |
| Derived 如何修改 / 實作 | override |
override |
| 適合情況 | Base 有合理預設行為 | Base 沒有合理預設行為 |
一句話記住:
virtual
→ 我有預設版本
→ 你想改可以 override
abstract
→ 我沒有預設版本
→ 你一定要提供實作
override
→ 好,我提供自己的版本
真正要先問的是:
Base Class 到底知不知道這個行為應該怎麼實作?
最後把 Day 13 與 Day 14 組合起來。
abstract class Animal
{
public string Name { get; private set; }
public int Age { get; private set; }
public Animal(
string name,
int age)
{
Name = name;
Age = age;
}
public abstract void MakeSound();
}
Animal 負責:
共同資料
→ Name
→ Age
共同規則
→ MakeSound()
class Dog : Animal
{
public int BarkVolume { get; private set; }
public Dog(
string name,
int age,
int barkVolume)
: base(name, age)
{
BarkVolume = barkVolume;
}
public override void MakeSound()
{
Console.WriteLine("汪");
}
}
class Cat : Animal
{
public int PurrVolume { get; private set; }
public Cat(
string name,
int age,
int purrVolume)
: base(name, age)
{
PurrVolume = purrVolume;
}
public override void MakeSound()
{
Console.WriteLine("喵");
}
}
List<Animal> animals = new List<Animal>
{
new Dog(
"Lucky",
3,
80
),
new Cat(
"Mimi",
2,
40
)
};
接著:
foreach (Animal animal in animals)
{
Console.WriteLine(animal.Name);
animal.MakeSound();
}
結果:
Lucky
汪
Mimi
喵
核心流程:
List<Animal>
↓
foreach
↓
Animal animal
↓
animal.MakeSound()
↓
實際 Object Type 是誰?
├── Dog → 汪
└── Cat → 喵
這就是:
Polymorphism
今天最重要的是:
virtual
→ Base 有預設實作
→ Derived 可以選擇是否改寫
abstract
→ Base 沒有預設實作
→ 規定 Derived 必須提供行為
override
→ Derived 提供自己的實作
而 Polymorphism:
共同 Base Type
↓
呼叫相同 Method
↓
實際 Object Type 不同
↓
執行不同實作
最重要的 Mental Model:
Animal animal = new Dog();
Variable Type
→ Animal
實際 Object Type
→ Dog
animal.MakeSound()
↓
Dog.MakeSound()
↓
汪
最後記住:
Override 是 Derived Class 提供自己的實作;Polymorphism 是透過共同的 Base Type 操作不同 Object,並執行實際 Object 對應的行為。