คำว่า Polymorphism มีใช้อยู่ในหลายวงการเลย แต่ในวงการซอฟต์แวร์ใน Wikipedia ถูกเขียนไว้ว่า
Polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.
😑 ผมเชื่อว่าถ้าไม่เคยรู้จักมันมาก่อนจริงๆอ่านไปก็ไม่เข้าใจหรอก แต่ก็ถอดหัวใจสำคัญของมันออกมาได้ว่า
Polymorphism คือทำให้ object เปลี่ยนรูปได้
สมมุติว่าเราต้องเขียน โปรแกรมบัญชีธนาคาร เหมือนเดิมละกัน โดยมันมี บัญชีออมทรัพย์ กับ บัญชีกระแสรายวัน โดยมีโค้ดแบบง่ายๆไว้ประมาณนี้
public class SavingAccount
{
public void Withdraw(double amount) { ... }
}
public class CurrentAccount
{
public void Withdraw(double amount) { ... }
}
แล้วสมมุติว่าเราต้องเขียนให้มัน ถอนเงิน ให้กับบัญชีทั้ง 2 แบบนี้ได้ล่ะ เราจะเขียนยังไง?
ถ้าเอาแบบง่ายก่อน ก็สร้าง method ให้มันถอนเงินได้ใช่ป่ะ ตามโค้ดด้านล่าง
static void Main(string[] args)
{
var sa = new SavingAccount();
Withdraw(sa, 500);
var ca = new CurrentAccount();
Withdraw(ca, 700);
}
static void Withdraw(SavingAccount account, double amount)
{
account.Withdraw(amount);
}
static void Withdraw(CurrentAccount account, double amount)
{
account.Withdraw(amount);
}
มันก็ทำงานได้นะ แต่ลองคิดดูว่าถ้าเรามีบัญชีอีกหลายๆประเภทเข้ามาล่ะ? เราจะมี method พวกนี้กี่ตัว?
ปัญหาที่เราเจออยู่ตอนนี้คือ object ที่เราสร้างขึ้นมา มันจะต้องใช้กับ data type ที่มันเป็นอยู่เท่านั้น มันเลยทำให้เราไม่สามารถส่ง object ของ SavingAccount ไปยัง CurrentAccount ได้ และทางตรงข้ามกันเราก็ส่ง object ของ CurrentAccount ไปยัง SaveingAccount ไม่ได้ด้วยเช่นกัน เพราะ object มันผูกกับ data type ที่มันเป็นอยู่นั่นเอง
ตามหัวข้อคือเราจะใช้ Polymorphism มาแก้ปัญหาในรอบนี้ยังไงล่ะ ซึ่งการใช้ใช้ Polymorphism ได้เราจะต้องใช้หลักของ Inheritance เข้ามาช่วยด้วยดังนั้นก็แก้โค้ดให้มันเป็นแม่ลูกกันหน่อยนึง ตามตัวอย่างในเรื่อง Inheritance เลย (ถ้าจำไม่ได้กดที่ชื่อเพื่อกลับไปทบทวนซะ) ซึ่งเราก็จะได้ออกมาราวๆนี้
public class BankAccount { ... }
public class SavingAccount : BankAccount { }
public class CurrentAccount : BankAccount { ... }
ซึ่งหลักจากที่เราทำ Inheritance มาเรียบร้อย เราจะได้ความสามารถของ Polymorphism มาด้วย นั่นคือ
{% hint style="success" %}
การเปลี่ยนรูปได้ (ผมชอบใช้คำว่าโค้ดมันดิ้นได้)
นั่นหมายความว่า object นั้นๆมันจะไม่ได้ผูกติดกับ data type ที่มันอยู่แล้วนั่นเอง
{% endhint %}
ดังนั้นเราจะสามารถเขียนโค้ดแบบนี้ได้
BankAccount acc1 = new SavingAccount();
BankAccount acc2 = new CurrentAccount();
จะเห็นว่าตัว acc1 และ acc2 ทั้งคู่มันเป็นคลาส์ BankAccount แต่มันก็สามารถเก็บ object ที่ไม่ใช่ BankAccount ได้นั่นเอง ดังนั้นเราก็สามารถที่จะทำให้โค้ดของเรารองรับการทำงานของ บัญชีหลายๆรูปแบบในอนาคตได้แล้ว โดยการเขียนโค้ดเพียงแค่ตัวนี้เท่านั้น
static void Withdraw(BankAccount account, double amount)
{
account.Withdraw(amount);
}
ไหนทดลองทำงานดูดิ๊
static void Main(string[] args)
{
BankAccount acc1 = new SavingAccount { OwnerName = "(Saving) Saladpuk" };
Withdraw(acc1, 500);
Console.WriteLine($"{acc1.OwnerName}, has THB {acc1.Balance}.");
BankAccount acc2 = new CurrentAccount { OwnerName = "(Current) Saladpuk" };
Withdraw(acc2, 700);
Console.WriteLine($"{acc2.OwnerName}, has THB {acc2.Balance}.");
}
Output
(Saving) Saladpuk, has THB 0.
(Current) Saladpuk, has THB -700.
จะเห็นว่ามันทำงานได้ตามปรกติแถมยังทำงานถูกต้องตามแต่ละประเภทบัญชีด้วย บัญชีออมทรัพย์ถอนเงินเกินในบัญชีไม่ได้ แต่บัญชีกระแสรายวันถอนเงินเกินได้
ก่อนอื่นผมอยากให้เข้าใจตรงกันก่อนว่า โดยปรกติตัวแปรมันจะทำงานตามที่ type ที่มันเป็นอยู่เท่านั้น เช่น เรามีคลาส Dog กับ Cat
public class Dog { }
public class Cat { }
ถ้าเราจะไปสร้าง object ของคลาส Dog ออกมา เราก็ต้องใช้ตัวแปรที่เป็นคลาส Dog มาเก็บมันเท่านั้น
Dog d1 = new Dog();
ไม่สามารถสร้าง object ของคลาส Dog ไปเก็บไว้กับตัวแปรที่เป็นคลาส Cat ได้เลย
Cat c1 = new Dog(); // Error
นี่แหละคือสิ่งที่เรียกว่า object มันผูกกับ data type ที่มันเป็นอยู่ นั่นเอง
การที่โปรแกรมของเราสามารถทำ Polymorphism ได้มันจะเป็นการ ปลดล๊อค การผูกกันที่ว่านี้ เลยทำให้โค้ดเรายืดหยุ่นขึ้น นำกลับมาใช้งานใหม่ได้ง่ายขึ้น โดยตัวหนึ่งที่เรานำมาทำให้เกิด Polymorphism ได้นั่นก็คือการทำ Inheritance นั่นเอง ดังนั้นเมื่อเราแก้โค้ดให้เป็นแบบนี้
public class Animal { }
public class Dog : Animal { }
public class Cat : Animal { }
มันเลยทำให้คลาส Dog และ Cat สามารถ เปลี่ยนรูป ตัวมันเองให้ตัวแปรที่เป็น Animal สามารถเก็บ object เหล่านั้นได้
Animal dog = new Dog();
Animal cat = new Cat();
จากตัวอย่างด้านบนคือการเปลี่ยนรูป จาก Dog หรือ Cat กลายเป็น Animal เลยทำให้ตัวแปร Animal สามารถทำงานร่วมกัน Dog และ Cat ได้นั่นเอง โดยมันมีกฏอยู่ว่า
{% hint style="warning" %} Base Class สามารถเก็บ Sub Class ได้ แต่ทำตรงข้ามกันไม่ได้ {% endhint %}
ถ้าเราแปลงโค้ดด้านบนเป็นรูปแล้วเราจะได้ออกมาเป็นแบบนี้
นั่นหมายความว่า Animal สามารถเก็บ object ที่เป็น Sub Class ของมันได้นั่นคือ Dog และ Cat แต่เราไม่สามารถทำตรงข้ามกันได้ นั่นคือ Dog และ Cat ไม่สามารถเก็บ object ที่เป็น Animal ได้นั่นเอง
Dog dog = new Animal(); // Error
Cat cat = new Animal(); // Error
และถ้าเรามองย้อนกลับไปถึงลำดับชั้นของการทำ Inheritance แล้วเราจะพบว่ามันเป็นรูปนี้
เลยเป็นผลว่าทำไม Object เลยสามารถเก็บตัวแปรทุกอย่างในโค้ดเราได้นั่นเอง (มันเป็น base class สูงสุดนั่นเอง)
object animal = new Animal();
object dog = new Dog();
object cat = new Cat();
นอกจากที่เราจะเปลี่ยนรูปจาก Sub class ไปให้ Base class เก็บไว้ได้แล้ว เรายังสามารถเปลี่ยนรูปมันกลับมาก็ได้นะ เช่น
public class Animal
{
public string Name { get; set; }
}
public class Dog : Animal
{
// พลักในการกัด
public int BitePower { get; set; }
}
static void Main(string[] args)
{
Animal animal = new Dog
{
Name = "Saladpuk",
BitePower = 50,
};
Dog dog = (Dog)animal;
Console.WriteLine($"Name: {dog.Name}, BitePower: {dog.BitePower}");
}
Output
Name: Saladpuk, BitPower: 50
จากโค้ดด้านบนจะเห็นว่าแม้ Dog จะถูกเปลี่ยนรูปไปเป็น Animal ในบรรทัดที่ 14 แล้ว แต่เราก็สามารถแปลงมันกลับมาเป็น Dog ได้ตามปรกติ ในบรรทัดที่ 19
อีกตัวอย่างหนึ่งในการทำ Polymorphism นั่นคือการใช้ Interface นั่นเอง เช่น เครื่องใช้ไฟฟ้าสามารถเปิด/ปิดได้ โดยมีทีวีและมือถือเป็นเครื่องใช้ไฟฟ้า
จากรูปเราก็สามารถทำ Polymorphism ได้เช่นกัน ตามนี้
public interface IElectronic
{
void TunrOn();
void TunrOff();
}
public class Television : IElectronic
{
public void TunrOff() => Console.WriteLine("Tv - off");
public void TunrOn() => Console.WriteLine("Tv - on");
}
public class Mobile : IElectronic
{
public void TunrOff() => Console.WriteLine("Mobile - off");
public void TunrOn() => Console.WriteLine("Mobile - on");
}
ลองใช้งานมันดู
IElectronic e1 = new Television();
e1.TunrOn();
e1.TunrOff();
IElectronic e2 = new Mobile();
var mobile = (Mobile)e2;
mobile.TunrOn();
mobile.TunrOff();
Output
Tv - on
Tv - off
Mobile - on
Mobile - off
{% hint style="info" %}
เกร็ดความรู้
Polymorphism มันมีหลามันมาจากคำ 2 คำรวมกันนั่นคือ (Poly = หลากหลาย) + (Morph = รูปร่าง) ดังนั้นพอรวมกันแล้วเลยเป็นกลายทำให้โค้ดของเรา เปลี่ยนรูป ได้นั่นเอง ซึ่งมันจะช่วยให้โค้ดของเรายืดหยุ่นขึ้นยังไงล่ะ
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=sG0C6PyW9e8" caption="" %}
{% embed url="https://www.youtube.com/watch?v=rTlmBkTXggE" caption="" %}