วันพฤหัสบดีที่ 10 กุมภาพันธ์ พ.ศ. 2554

ความหมายของ Abstract Class กับ Interface คือ?


ความหมายของ  Abstract Class กับ Interface คือ?

- Interface ประกอบไปด้วย abstract method เท่านั้น no state, no value, no implementation
- Abstract class เป็นคลาสที่มี abstract method อย่างน้อยหนึ่งเมธอด และส่วนใหญ่จะมี concrete method (เมธอดที่มีส่วน implementation แล้ว) ด้วยก็ได้ นอกจากนั้น abstract class ยังสามารถมี state (พวก attribute หรือ instance variable ต่างๆ) ได้ด้วย
- Concrete class ใดๆที่ได้ inherit จาก abstract class หรือได้ implement interface ใดๆไป ก็ย่อมต้อง implement พวก abstract method เหล่านั้นให้ครบถ้วนสมบูรณ์ จึงจะถือว่า class นั้นเป็น concrete class และสามารถสร้าง instance จาก class นั้นได้
- Class ใดๆก็ตามจะสามารถ inherit จาก abstract class ได้เพียงหนึ่งเท่านั้น แต่จะสามารถ implement interface ได้มากกว่าหนึ่ง (เคยสงสัยมั้ยครับว่าเพราะอะไร นี่แหล่ะครับที่เป็นส่วนหนึ่งที่ทำให้ concept ในการใช้งานระหว่าง interface กับ abstract class แตกต่างกันโดยสิ้นเชิง)
ฟังดูเหมือนว่าไม่แตกต่างกัน ฟังดูเหมือนว่าทั้งสองอันนำมาใช้แทนกันได้ และดูเหมือนว่า Interface มันก็คือ abstract class ที่มีแต่ abstract method และไม่มี instance variable ใดๆเลย ซึ่งก็ใช่ครับ ในภาษา OOP ยุคเก่าๆบางภาษา เช่น C++ นั้น ไม่มี interface ให้ใช้งานหรอกครับ ไม่มีแม้กระทั่ง keyword interface เลยด้วยซ้ำ (ไม่เหมือนกับภาษา Java หรือ VB.net ที่สนับสนุน interface มาตั้งแต่แรกดั้งเดิมในตัวภาษาเลย) แต่โปรแกรมเมอร์ภาษา C++ ก็ยังสามารถใช้ประโยชน์ในเรื่อง interface ได้ ก็ด้วยการใช้ abstract class ที่มีแต่ abstract method นี่ยังไงหล่ะครับ
ทั้งหมดนั้นคือเชิง Technical ครับ ทีนี้มาถึงจุดสำคัญแล้วครับ เรื่องของ concept ที่ทำให้การใช้งาน interface และ abstract method แตกต่างกัน ต้องขอออกตัวไว้ก่อนว่า ผมอธิบายใครไม่ค่อยเป็นเท่าไร ยิ่งเป็นเรื่อง concept เนี่ยก็รู้ๆกันอยู่คับว่ามันเข้าใจยาก ถ้าหากใครอ่านจบแล้ว ทำหน้างงๆ ไม่เข้าใจที่ผมอธิบาย ผมก็ต้องขออภัยไว้ ณ ที่นี้ด้วยครับ
Interface ถ้าแปลกันตรงๆก็อาจจะหมายความว่า "การติดต่อประสาน" ในโลกของความเป็นจริง (และในโลกของ OO) คุณจะพบ interface ได้มากมายในชีวิตประจำวันครับ ยกตัวอย่างเช่น
- คุณกับผม คุยกันรู้เรื่อง เพราะอะไร? เพราะเรามี Interface เรื่องภาษาที่ตรงกัน สมมติว่าเป็น interface IThaiLang ก็แล้วกัน คุณกับผมต่างก็เข้าใจ IThaiLang นี้เหมือนกัน (มองในมุมโปรแกรมมิ่งก็คือ คุณกับผมต่างก็ implement IThaiLang นี้เหมือนกัน) แน่นอนย่อมต้องคุยกันรู้เรื่อง ผมเดินๆไปไปเจอฝรั่งจ๋าคนนึง เผอิญฝรั่งจ๋าคนนั้นพูดไทยไม่ได้ (ไม่ได้ implement IThaiLang นะสิ) แน่นอนผมกับฝรั่งจ๋าคนนั้นก็สื่อสารกันไม่ได้ เพราะไม่เข้าใจ interface ที่ตรงกัน
- รถยนต์ทุกคันต่างก็หยุดจอดที่สี่แยกเมื่อเห็นสัญญาณไฟแดง เพราะอะไร? เพราะรถยนต์คันนั้น (อันที่จริงคือคนขับรถยนต์คนนั้น) เข้าใจว่า สัญญาณไฟสีแดง หมายถึง ให้หยุดรถ นั่นแสดงว่า รถยนต์มีความเข้าใจที่ตรงกันกับป้ายสัญญาณไฟจราจรหรือสามารถติดต่อสื่อสารกันได้ สมมติว่ารถยนต์คันใดก็ตามที่จะเข้าใจสัญญาณไฟจราจรได้จะต้องเข้าใจ interface ITrafficLight (หรือ implement ITrafficLight นั่นเอง) ทีนี้เมื่อรถยนต์ต่างๆวิ่งมาถึงสี่แยกที่มีป้ายสัญญาณไฟจราจรอยู่ ป้ายสัญญาณไฟจราจรเหล่านั้นก็จะส่ง message ไปบอกรถยนต์ต่างๆว่าตอนนี้ไฟจราจรเป็นสีอะไร message ที่ส่งไปนี้ส่งไปโดยใช้ interface ITrafficLight เพราะฉะนั้นรถยนต์คันใดก็ตามที่ implement ITrafficLight นี้ก็จะเข้าใจและหยุดรถเมื่อเห็นสัญญาณไฟสีแดง ในทางกลับกันหากรถยนต์คันไดที่ไม่ได้ implement ITrafficLight นี้ ก็ย่อมจะไม่เข้าใจใน message ที่ส่งมา และแน่นอนย่อมส่งผลให้ขับฝ่าไปแดงไปและถูกหมาต๋าซิวไปในที่สุด
- เมื่อเข้าหน้าหนาว(เหมือนอย่างตอนนี้) อากาศจะหนาว อุณหภูมิจะลดลง ร่างกายคุณย่อมจะรู้สึกว่าหนาว ขนลุก ตัวสั่น เป็นเวลาเดียวกันกับที่ สารปรอทในเทอร์โมมิเตอร์หดตัวลง เพราะอะไร? แน่นอนเพราะคุณและสารปรอทต่างก็เข้าใจว่าอุณหภูมิมันลดลง สมมติว่าออปเจคต์ใดก็ตามที่จะเข้าใจได้ว่าอุณหภูมิลดลงจะต้อง implement ITemperature แสดงว่าทั้งคุณและปรอทในเทอร์โมมิเตอร์ต่างก็ implement ITemperature นี้เหมือนๆกัน และที่คุณและปรอทรู้ว่าอุณหภูมิได้ลดลงแล้วก็เพราะว่าออปเจคต์ Weather ได้ส่ง message มาบอกคุณและปรอทผ่านทาง interface ITemperature นี้เอง ซึ่งคุณและปรอทต่างก็เข้าใจมันได้ดี แต่จะตอบสนองต่อ message นี้อย่างไรก็ขึ้นอยู่กับออปเจคต์มันเอง เช่น คุณอาจตอบสนองโดยการ ตัวสั่น ปากซีด ขนลุก แต่อีกทางหนึ่ง ปรอทตอบสนองต่อ message นี้ด้วยการหดตัว พันธะระหว่างอะตอมแน่นหนาขึ้น ระยะพันธะหนสั้นลง เป็นต้น
จากตัวอย่างทั้งหมดนี้ คุณสังเกตเห็นอะไร
- ออปเจคต์หนึ่งๆอาจถูกสร้างขึ้นมาเพื่อให้สามารถติดต่อสื่อสารกับออปเจคต์ใดๆโดยผ่านทาง interface หนึ่งๆ
(ออปเจคต์คนไทยคนหนึ่งติดต่อสื่อสารกับออปเจคต์คนไทยอีกคนหนึ่งโดยผ่าน interface IThaiLang)
(ออปเจคต์รถยนต์สามารถเข้าใจ message จากป้ายสัญญาณไฟจราจรได้ผ่านทาง ITrafficLight)
จากที่คุณสังเกตเห็น คุณคงจะพอมองออกแล้วว่า interface เหมือนเป็นข้อตกลง (protocol) เพื่อให้ออปเจคต์สามารถติดต่อสื่อสารกันได้ interface บอกถึงลักษณะของช่องทางการสื่อสารว่ามีมาตรฐานเป็นอย่างไร ในเชิงของโปรแกรมมิ่ง interface อาจจะเป็นข้อตกลงที่บอกถึงลักษณะของ message ที่ออปเจคต์จะได้รับหรือส่งไป หรือลึกกว่านั้นก็คือ บอกถึงว่า message ที่จะรับหรือส่งนั้นมี parameter เป็นอย่างไร data type ไหน return ค่าอะไร เป็นต้น
นอกจากนั้นคุณจะสังเกตเห็นว่า ออปเจคต์ที่ implement interface หนึ่งๆ ไม่ได้หมายความถึงความสัมพันธ์แบบ IS-A เฉกเช่นการสืบทอดแบบ inheritance ทั่วๆไป เช่นคุณบอกไม่ได้ว่า "ออปเจคต์คนไทย is a IThaiLang" หรือ "ออปเจคต์รถยนต์ is a ITrafficLight" หรือ "ออปเจคต์ปรอท is a ITemperature" เป็นต้น ต่างจากการสืบทอดแบบ inheritance ทั่วไป เช่นใน คห.7 ซึ่งคุณจะบอกได้โดยสามัญสำนึกว่า "ออปเจคต์ Men is a Person" หรือ "ออปเจคต์ Women is a Person"  และในเมื่อความสัมพันธ์ไม่ได้เป็น IS-A เหมือนอย่าง classical inheritance เพราะฉะนั้นคนส่วนใหญ่มักจะไม่ใช้คำว่า inherit from interface แต่จะใช้ว่า implement interface มากกว่า เมื่อคลาส A เข้าใจใน interface I เราจะเรียกว่า "class A implement interface I" ไม่ใช่ "class A inherit from interface I" และหากคุณสังเกตตั้งแต่ต้น ผมจะใช้คำว่า implement กับ interface เท่านั้น ซึ่งนี่คือเหตุผล
นอกจากนั้นคุณจะพบว่าออปเจคต์ใดๆอาจจะ implement interface ได้มากกว่าหนึ่ง ซึ่งแน่นอนว่ามันควรจะเป็นอย่างนั้น เพราะออปเจคต์อาจจะต้องติดต่อสื่อสารกับออปเจคต์ภายนอกได้มากมายอยู่แล้ว เพราะฉะนั้นการมี interface ไว้ติดต่อสื่อสารกับภายนอกมากกว่าหนึ่งจึงไม่ใช่เรื่องแปลก กลับกันในภาษาที่ไม่นิยม multiple inheritance คุณจะพบว่าคุณไม่สามารถ inherit จาก abstract class ได้มากกว่าหนึ่ง เพราะมันผิดกฏตัวภาษา (ก็ภาษามันไม่นิยม multiple inheritance นี่นา)
ทั้งหมดคือเรื่องของ interface (อันที่จริงผมควรจะอธิบายเรื่อง abstract class ก่อน interface นะ) ทีนี้ในเรื่องของ abstract class เนี่ยผมเข้าใจว่าคุณคงจะพอเข้าใจบ้างอยู่แล้ว (อันที่จริงเริ่มขี้เกียจพิมพ์แล้ว ฮา~)
abstract class ก็คือ class (ไม่ได้กวนนะ) เพราะฉะนั้นสิ่งที่คุณรู้เกี่ยวกับเรื่อง class inheritance ทั่วๆไป ก็ไม่แตกต่างกับการ inheritance จาก abstract class 
คร่าวๆก็คือ abstract class คือคลาสที่คุณยังไม่รู้หรือยังไม่แน่ใจว่างานบางอย่างจะต้อง implement ยังไง ซึ่งคุณก็จะปล่อยให้ method เหล่านั้นเป็น abstract ไป และปล่อยให้คลาสลูกไป implement กันเอาเอง การที่คุณจะ inherit จาก abstract class อะไร คุณควรจะต้องนึกถึงความสัมพันธ์แบบ IS-A ด้วย อย่า inherit มั่วๆ เพราะจะทำให้โครงสร้างมันเสีย ส่วนใหญ่ abstract class มักจะเกิดจากการที่คุณสร้างๆ(หรือออกแบบ)คลาสไปแล้วสังเกตเห็นถึงความสัมพันธ์ระหว่างสองคลาสหรือมากกว่าว่ามีบางอย่างที่เหมือนๆกัน มี method เดียวกัน (แต่อาจจะ implement ไม่เหมือนกัน) คุณก็จะดึงพวกที่เหมือนๆกันเหล่านั้นขึ้นไปเป็น abstract class และปล่อยให้บาง method เป็น abstract ไป แต่อันที่จริงการทำแบบนี้มันไม่ดีแน่นอน สิ่งที่คุณควรทำก็คือ ออกแบบระบบทั้งหมดให้ดีก่อนตั้งแต่ต้น พยายามนึกถึง top-down design คุณคิดว่าในระบบของคุณจะต้องมีคลาสอะไรบ้าง แล้วคลาสอะไรที่พอจะจับเป็นหมวดหมู่เดียวกันได้ ก็สร้างเป็น base class ไป พยายามจัดหมวดหมู่ให้มากที่สุดเท่าที่จะทำได้ base class บางอันของคุณที่ได้อาจจะแค่เป็นต้นแบบให้กับคลาสลูกอื่นๆก็ไม่เป็นไร method ไหนของ base class ที่ no idea to implement ณ จุดนั้นก็ปล่อยเป็น abstract ไป แล้วปล่อยให้เหมือนกับเป็นการบังคับว่าคลาสใดที่จะมาเป็นลูกคลาสนี้จะต้อง make sense กับ method พวกนั้นด้วยเท่านั้นเอง (รู้สึกจะคร่าวๆเกินไป แต่คิดว่าคงเข้าใจดีอยู่แล้วนะครับ)