Interface Inheritance #3: Multiple Inheritance

   เรื่องนี้กำเนิดมาจากความฝันของผู้คิดค้นภาษา C++ อยากที่จะ reuse class ต่างๆ ให้อยู่ในระดับสูงสุดจึงออกแบบภาษาให้รองรับการ Inherit มาจากหลาย class ได้ ยกตัวอย่างเช่นถ้าเรามี class องุ่น และมี class ผลไม้แห้ง เราสามารถสร้าง class ลูกเกตโดยดึงเอาความสามารถของทั้งสอง class เอามาใช้ได้ ฟังดูดีใช่ไหมครับ แต่คุณรู้รึเปล่าว่า เรื่อง Multiple Implementation  Inheritance กลับกลายเป็นจุดบอดที่สุดของภาษา C++ เลย โดยเนื้อหาแล้วมันดูดีเกินกว่าการเอาไปใช้จริงครับ กล่าวคืองานที่ประยุกต์ใช้ Multimple Implementation Inheritance นั้นมีน้อยมาก ภาษา C++ เพื่อทำให้รองรับ Multiple Implementation Inheritance นั้นต้องปรับ syntax ขนานใหญ่ ทำให้ถึงแม้ว่าเราไม่ได้ใช้มัน ก็ต้องโดนกระทบไปด้วย นั่นเป็นสาเหตุที่ภาษา C++ ถึงเรียนรู้ยากมาก

    ยังไม่เท่านั้นนะครับ C++ ไม่ได้รองรับ Multiple Inheritance ได้อย่างสมบูรณ์นัก จะโทษว่า C++ ไม่ดีก็คงไม่ได้ ปัญหาจริงๆ มันอยู่ที่แนวคิดของ Multiple Inheritance ยังคิดกันไม่เสร็จเลย ถ้าเลี่ยงได้ก็ให้เลี่ยงครับ

    พอมาถึงภาษา Java ก็ได้เล็งเห็นแล้วว่า Multiple Implementation Inheritance นั้นมีโทษมากกว่าคุณ จึงตัดเอาออกไปครับ Java และ C# จึง Inherit ได้จาก class เดียว

    วันนี้เรามาเรียนเรื่อง Multiple Inheritance จากการ Interface ก็ต้องคุยให้ชัดครับว่ามันเป็นคนละเรื่องกับที่เราคุยกันข้างบน เรามาดูกันดีกว่าว่าแนวคิดของ Multiple Interface Inheritance มันเป็นยังไง

    อย่างที่เคยบอกครับว่ามนุษย์เราคิดอะไรไม่พ้นสิ่งรอบๆ ตัว เช่นการที่เราใช้เลขฐาน 10 ก็คงไม่มีอะไรมากไปกว่ามนุษย์เรามี 10 นิ้ว อะไรทำนองนี้ เรื่องของ Interface ก็เหมือนกัน เราได้แนวคิดมาจากสิ่งรอบๆ ตัวครับ ไม่ต้องไปอื่นไกลก็ตัวมนุษย์เองนี่แหละ ยกตัวอย่างนะครับ คุณเองตื่นมาตอนเช้า เจอน้อง คุณก็คุยโผงผาง พูดไปเตะก้นมันไป นี่เป็น Interface แบบหนึ่งใช่ไหมครับ เมื่อเวลาทานข้าวเช้าคุณก็เจอแม่ interface ของคุณกับแม่ก็ต้องเปลี่ยนไป จะให้เหมือนที่ทำกับน้องได้ยังไงหละครับ เดี๋ยวไม่ได้ตังค์ใช้ จากนั้นคุณไปโรงเรียน เจอครู เจอเพื่อน เจอแม่ค้าขายข้าวแกง เจอกระเป๋ารถเมล์ แบบนี้ก็ล้วนแต่แตกต่างกันออกไปตามแต่ละบทบาทที่ชีวิตคุณดำเนินอยู่ ถ้าผมถามว่า interface ไหนเป็นตัวตนของคุณจริงๆ คุณก็คงตอบว่าก็ทุก interface นั่นแหละ จริงไหมครับ เห็นจุดที่ผมอยากบอกคุณหรือยัง ในโลกของ OOP คุณเองก็ถือว่าเป็น object หนึ่ง แต่คุณมีได้หลาย interface จริงหรือเปล่าครับ และมันยังไม่หมดเท่านี้ ถ้าคุณต้องเจอคนใหม่ๆ ที่เผ่านเข้ามาใช้ชีวิตคุณ คุณอาจต้องสร้าง interfaces เพิ่มขึ้นเรื่อยๆ เพื่อรองรับมัน จริงไหมครับ

    มองให้ใกล้ตัวหน่อย เอาโทรทัศน์ก็แล้วกัน ในยุคปัจจุบัน มันก็มีหลาย interface เช่น remote, เปลี่ยนช่องหน้าเครื่อง, ต่อกับ Video, ต่อออกเครื่องเสียง และอื่นๆ อีกมากมาย จะเห็นได้ว่าการที่ object หนึ่งจะมีหลาย interface ถือว่าเป็นเรื่องธรรมดามากครับ การทำ Multiple Inheritance สำหรับ interface นั่นก็คือ การเพิ่ม Interface ให้กับ object นั่นเอง และทำให้ object นั้นมีคุณสมบัติ Polymorphism เพื่อจะ reuse code ตัวเรียก อย่างที่เราเรียนมาในบทก่อนๆ นั่นเอง

    class ในภาษา C# นั้นสามารถ inherit มาได้ class อื่นได้เพียง class เดียวและ สามารถ มี interface ได้หลาย interface  ดูตัวอย่างครับ

interface IBackup
{
	void Backup();
	void Restore();
}
interface IProcess
{
	void Run();
	void Stop();
}

class Base
{
 	virtual public void func() { } 
}
class Derive : Base, IBackup, IProcess
{
 	override public void func() { }
	void IBackup.Backup() { }
	void IBackup.Restore() { } 
	void IProcess.Run() { }
	void IProcess.Stop() { }
	public static void Main() { }
}

    จากตัวอย่างนี้จะเห็นได้ว่า class Derive นั้นทำ Implementation Inheritance มาจาก Base และ มีสอง Interfaces คือ IBackup และ IProcess

    วิธีการใช้งานก็ปกติแหละครับ ถ้าอยากได้ interface ไหนก็ cast เป็น interface นั้น โดยปกติแล้ว code function หนึ่งจะยุ่งอยู่แค่ interface เดียวเท่านั้นแหละครับ

การ Inherit มาจาก class ที่มี Interface

multiple.cs

using System;
interface IBackup
{
	void Backup();
	void Restore();
}
interface IProcess
{
	void Run();
	void Stop();
}
class Base : IBackup
{
	void IBackup.Backup() { Console.WriteLine("Base Backup"); }
	void IBackup.Restore() { Console.WriteLine("Base Restore"); } 
 	virtual public void func() { } 
}
class Derive : Base, IBackup
{ 	override public void func() { }
	void IBackup.Backup() { Console.WriteLine("Derive Backup"); }
	void IBackup.Restore() { Console.WriteLine("Derive Restore");  } 
}
class Start
{ 	public static void Main()
	{
		Derive x = new Derive();
		IBackup y = x;
		y.Backup();
	}
}

DOS Prompt

C:\CS>multiple
Derive Backup

C:\CS>_

    จากตัวอย่างนี้จะเห็นได้ว่า Derive ต้องการที่จะทำ Implementation Inheritance จาก Base ซึ่ง Base เองนี้รับเอา Interface แบบ IBackup มาใช้ มันเป็นกฏเลยครับว่า Derive จะต้อง Inherit IBackup มาเองด้วย จะถือว่า Base มีให้แล้วไม่ได้ครับ และ method Backup() หรือ Restore() จะมีก็ได้ไม่มีก็ได้ใน Derive ถ้าไม่มีมันจะไปใช้ของ Base เมื่อเรามองในมุมมองของ IBackup แต่ถ้า Derive จะเพิ่ม code ของ Backup() หรือ Restore() เอง ก็ห้ามใช้ override นะครับ เพราะ IBackup ถูกบังคับให้ inherit มา Derive ด้วย พอเห็นภาพไหมครับ

Interface Inherits Interface

 

interface ICar
{
	void CarMethods();
}
interface IPerson
{
	void PersonMethods();
}
interface ITruck : ICar, IPerson
{
	void TruckMethods();
}

class  Logistics : ITruck
{
	void ICar.CarMethods() { }
	void IPerson.PersonMethods() { }
	void ITruck.TruckMethods() { }
}
class Start
{ 	
	public static void Main()
	{
		ICar icar;
		IPerson iperson;
		ITruck itruck;
		Logistics l = new Logistics();
		icar = l;
		iperson = l;
		itruck = l;
		icar.CarMethods();
		icar.PersonMethods();
		icar.TruckMethods();
		iperson.CarMethods();
		iperson.PersonMethods();
		iperson.TruckMethods();
		itruck.CarMethods();
		itruck.PersonMethods();
		itruck.TruckMethods();
	}
}

     (สีแดงคือ error นะครับ เรียกใช้ไม่ได้)    

    จากตัวอย่างครับ เป็นการ reuse interface ที่เคยมีมาก่อน คือเราต้องการสร้าง interface สำหรับรถบรรทุก เรา inherit มาจาก interface ของ รถ และคน ดังนั้น interface ITruck จึงมีทั้ง ของตัวเอง, ICar และ IPerson

    ที่สำคัญคือการนำไปใช้ครับ ระบบ Logistic ซึ่งเป็นระบบส่งสินค้ามีการใช้รถบรรทุก จึง Inherit เอา Interface ของ รถบรรทุกไป เวลา implement interface ต้ัองมองแยกนะครับว่าเป็น ITruck, ICar, IPerson มองรวมเป็น ITruck เลยไม่ได้ พอเวลาเราสร้าง object จาก class นี้ เรามองได้เป็น default Interface (Logistics) , ICar, IPerson และ ITruck ได้เลยครับขึ้นอยู่กับว่าอยากมองเป็นอะไร มีข้อน่าสังเกตหน่อยว่า ถ้ามองผ่าน Interface แบบ Itruck จะเห็น Methods ของ ICar และ IPerson ได้ด้วยครับ