Implementation Inheritance #2 

It has no license to break Encapsulation

    ในบทที่แล้วเราได้เรียนรู้ถึงความรู้เบื้องต้นเกี่ยวกับ Implementation Inheritance และ C# Syntax อย่างง่ายที่เอามารองรับกับเรื่องนี้ ในบทนี้เราจะมีเรียนรู้กันต้องเรื่องการเข้าถึง method และ property ของ Base Class กัน

    อย่างที่ผมเขียนไว้ในบทที่แล้ว ว่า C# และ Java ไม่รองรับความสัมพันธ์แบบ HAS-A ในบทนี้เรามาดูกันว่าไม่รองรับอย่างไร เรามาดู code กัน

 

im1.cs

using System;
class Base 
{ 	public void func()
	{
		Console.WriteLine("Hello, World");
	}
}
class Derive : Base
{
	public void func2()
	{
		Console.WriteLIne("Derive, func2()");
	}
}
class Start
{ 	public static void Main()
	{ 	
		Derive x = new Derive();
		x.func();
		x.func2();
	}
} 

DOS Prompt

C:\CS>im1
Hello, World
Derive, func2()

C:\CS>_

 จะเห็นได้ว่า เราสร้าง class Derive ใหม่ขึ้นมา โดยการทำ Implementation Inheritance มาจาก class Base นั่นก็ทำให้เราได้ func() มาจาก class Base และ ในตัวของมันก็สร้าง func2() ขึ้นอีก ทำให้เวลาเราเรียกใช้งาน class Derive ทำให้เราสามารถใช้งานได้ทั้ง func() และ func2() ดูดีไหมครับ

   แต่ถ้าคุณสังเกตดี คุณจะเห็นว่า func1() ของ class Base ถ้าคุณเลือกที่จะ inherit ไปยัง class Derive ยังไงมันก็คงอยู่ที่ class Derive เสมอ คุณไม่มีทางจะไม่เอามันออกไป และที่จะเลือกเอาเฉพาะ Method/Property ที่คุณต้องการมา inherit ไปยัง class Derive ดังนั้นความสัมพันธ์มันจึงเป็นแบบ IS-A เท่านั้น เพราะ ความสัมพันธ์แบบ IS-A เป็นความสัมพันธ์แบบที่ ทุกอย่างใน Class Base จะต้องมีใน class Derive ส่วนใน class Derive จะเพิ่มเติมอะไรก็แล้วแต่ 

    ส่วนความสัมพันธ์แบบ HAS-A นั้น บางส่วนเท่านั้นใน class Base จะถูก inherit ไปยัง class Derive  แต่ Implementation Inheritance ของ C# ถ้าคุณใช้ คุณจะต้อง inherit Method/Property ทุกตัว ดังนั้นคุณจะได้ Method/Property ที่ไม่พึงประสงค์ติดการ Inherit ไปด้วย ใช้คุณเอาไปใช้งาน รับรองว่าวุ่นวายมากครับ ผมจึงบอกว่ามันไม่รองรับแบบ HAS-A แต่ถ้าคุณควบคุมมันได้ ก็ใช้ได้ครับ

    นั่นเป็นสิ่งที่ต้องรู้เรื่องแรก คราวนี้เรามาดูเรื่องที่สองกัน คือการ inherit นั้นจะ inherit ได้เฉพาะ Method และ Property เท่านั้น ส่วนที่เป็น Private function / ตัวแปร นั้นไม่สามารถ inherit ได้ แบบนี้ดับฝันของโปรแกรมเมอร์ทั่วไปหมดเลย ผมเชื่อว่า คุณคงอยากได้แบบนี้ครับ

class Robot 
{ 	
	private void job1() { }
	private void job2() { }
	private void job3() { }
	private void job4() { }

	public void work()
	{
		job1();
		job2();
		job3();
		job4();
	}
}
class Myrobot : Robot
{
	private void myjob1() { }
	public void work()
	{
		job1();
		job2();
		job3();
		myjob1();
		job4();
	}
}

    ผมเชื่อว่าคุณคงอยากได้แบบนี้คือ สมมุติว่าคุณมี class Robot เพื่อสั่งให้หุ่นยนต์ทำงาน ใครก็ไม่รู้เขียนให้มันทำงาน work() โดยทำงานเรียงกัน job1() ... job4() คุณก็ว่ามันเป็น Method ที่ใช้ได้ แต่มันก็ไม่ตรงตามความต้องการของคุณ คุณต้องการปรับปรุง work() นั้นแต่ก็ไม่อยากจะไปแก้ code ของเขาโดยตรง (ยิ่งเป็น .dll ก็ไม่มีทางแก้ code ครับ) คุณเลย inherit มาเป็น Myrobot แล้วคุณก็เขียน Method work() ใหม่ โดยการ แทรก code ของคุณเข้าไป คือ myjob1() แต่ผลปรากฏว่า ทำไม่ได้ครับ เพราะ job1() .. job4() นั้น เป็น private และ private จะไม่ถูก inherit มายัง derived class ครับผม

    ทำไมถึงเป็นเช่นนั้นคุณอาจสงสัย เพราะถ้าปล่อยให้ derived class มองเห็นทุกอย่างของ base class ได้ มันน่าจะดี เราน่าจะสร้าง class ใหม่ซึ่งปรับปรุงความสามารถของ class เดิมได้อย่างไร้ขีดจำกัด

    ที่เป็นอย่างนี้เพราะ ถ้าคุณยอมให้ Derived class ซึ่งเป็น class ข้างนอก ถึงแม้ว่ามันจะ inherit มาก็เถอะ มาอ่านเขียนตัวแปร/function ที่เป็น private ได้ มันจะเสียคุณสมบัติของ Encapsulation ทันที คือมันไม่สามารถซ่อนสิ่งของที่ใช้เฉพาะใน class เราจึงอาจพูดได้ว่า Implementation Inheritance นั้นก็ไม่ได้รับสิทธิพิเศษให้ทำลาย Encapsulation ครับ ฟังดูมันอาจจะยังดูไม่มีเหตุผลเพียงพอในการควบคุมไม่ให้ Derived Class เข้าถึง ข้อมูล private ใน Base Class ผมทิ้งเอาไว้ในบท Implementaion Inheritance : The Devil ดีกว่าครับ ในบทนั้นผมจะชี้ให้ดูให้เห็นชัดเจนเลยครับว่าทำไม

    เรามาดูทางแก้ของ Java/ C# (รวมทั้ง C++) คือสร้าง Modifier ตัวใหม่ใช้ทดแทน public และ private นั่นก็คือ protected ซึ่งถ้า function หรือ ตัวแปรไหนเป็น protected ภายนอกจริงๆ จะรู้สึกเหมือนว่าเป็น private แต่ถ้าเป็น class ที่ derived ไป จะถูก inherit ไปด้วยราวกับว่าเป็น public ดังนั้น code ต้องเปลี่ยนไปดังนี้ครับ ต้องเปลี่ยนเป็นดังนี้ครับ

   

class Robot 
{ 	
	protected void job1() { }
	protected void job2() { }
	protected void job3() { }
	protected void job4() { }

	public void work()
	{
		job1();
		job2();
		job3();
		job4();
	}
}
class Myrobot : Robot
{
	private void myjob1() { }
	public void work()
	{
		job1();
		job2();
		job3();
		myjob1();
		job4();
	}
}

    แล้วเราจะมาคุยกันเกี่ยวกับ protected อีกทีในบทที่ว่าด้วย Implementation Inheritance : The Devil