Screen Shot 2558-04-24 at 1.40.26 PM
เห็นมีการพูดถึงเรื่อง Composition over Inheritance ใน facebook

มันน่าสนใจดีนะ แต่ผมก็ไม่ค่อยรู้เรื่องมันเท่าไร
ส่วนเรื่อง OOP นี่ไม่ต้องพูดถึง …
ดังนั้นลองมาทำความเข้ากับมันหน่อยสิ
ว่าเป็นอย่างไรกัน ?

ในหนังสือ Design Pattern โดย Gang of Four (GoF)

กล่าวไว้ว่า

“Favor object composition over class inheritance.”

มาทำความเข้าใจกันหน่อยว่า มันคืออะไรหว่า ?

Inheritance มันเป็น
วิธีการที่ดีสำหรับการเปลี่ยนพฤติกรรมการทำงาน
ซึ่งทำให้เราสามารถสร้างระบบงานได้เรียบง่าย
อยากเปลี่ยนพฤติกรรมการทำงานของแต่ละ subclass ก็ทำได้เลย
เหมือนการทำ plug-in เข้าไป
หรือว่า เราต้องไปใช้พวก library หรือ black-box system
วิธีการ inheritance จะมีประโยชน์มากๆ

แต่ว่ามันเปราะบางมากๆ นะ !!
เพราะว่า subclass นั้นสามารถทำอะไรได้หลายอย่าง
เช่น การเรียกใช้ override method
บ่อยครั้งพบว่า มีการใช้งาน และทำงานผิดพลาด
รวมทั้งจะพบว่า subclass จะผูกติดกับ base case อย่างมากมาย (Tight coupling)

และที่สำคัญ Reuse != Inheritance นะครับ !!
Screen Shot 2558-04-24 at 1.31.44 PM

คำถามคือ
ถ้าต้องการเปลี่ยนพฤติกรรมของ base class แล้วมันจะส่งผลกระทบอะไรบ้าง ?
คำตอบคือ
พวก subclass โดนหมดสินะ … น่ากลัวไหมนะ ?
ตรงขึ้นอยู่กับ context แล้วล่ะนะ

แล้วเราจะลดเรื่อง Tight coupling ได้อย่างไรล่ะ ?

วิธีการหนึ่งคือ composition ยังไงล่ะ
สามารถแยกการทำงานส่วนต่างๆ ออกจากกัน
ถ้าต้องการทำอะไร ก็นำส่วนการทำงานต่างๆ มารวมกัน
เหมือนกับทีมฟุตบอล ที่แต่ละคนก็มีหน้าที่ และ ทำงานของตัวเองไป (Single Responsibility Principle)
แต่เมื่อถึงการแข่งขันจะต้องมารวมกัน และ ทำงานด้วยกัน (Composition)
อยากจะเปลี่ยนใครเข้า หรือ ออก ก็ไม่ใช่เรื่องยากอะไร
รวมทั้งสามารถทดสอบการทำงานในแต่ละส่วนได้ง่ายอีกด้วย

ความเข้าใจผิดที่เกิดขึ้นมากๆ ก็คือ

composition นั้นไม่ได้ใช้หรือทำงานร่วมกับ inheritance
แต่ในความเป็นจริง
composition นั้นใช้งานร่วมกับ inheritance บ่อยมากๆ

แต่ในการ implement ทั่วไป
เรามักจะสร้าง interface เล็กๆ ขึ้นมา
และจะไม่ inherit มาจาก class ใหญ่ๆ
ซึ่งตัวอย่างที่ชัดเจนก็คือ Java Listener นั่นเอง
คุณสามารถทำการสร้าง class ที่ทำการ implement จาก listner interface
หรือทำการ inherit มาจากสิ่งที่เรียกว่า Adapter ได้

ยิ่งอ่านยิ่งงงไปกันใหญ่ !!

มาดูตัวอย่างการใช้งานร่วมกันระหว่าง Composition กับ Inheritance ดีกว่า

เป็นตัวอย่างจากหนังสือ Agile Software Development
ซึ่งนำมาดัดแปลงเล็กน้อย ดังนี้

ตัวอย่างมีรายละเอียดดังนี้
ระบบพนักงานของบริษัทแห่งหนึ่ง
มีพนักงาน 2 กลุ่มคือ

  • กลุ่มที่ 1 จ่ายแบบเงินเดือน
  • กลุ่มที่ 2 จ่ายแบบรายชั่วโมง

ต้องการสร้างระบบ report การจ่ายเงินของแต่ละกลุ่มออกมา
จะทำอย่างไรดีล่ะ ?

เริ่มต้นด้วยการสร้าง Employee ก่อนสิ
ต้องสร้าง subclass ที่ inherit มาจาก Employee 2 class คือ

  • HourEmployee
  • SalaryEmployee

นั่นคือการใช้งาน Inheritance ใช่ไหม ?
สามารถเขียน code ได้ดังนี้

โดยในการสร้างรายงานนั้น
จะต้องทำการวนข้อมูลของผู้ใช้งานทุกๆ คน
เรียกใช้งานผ่าน method generateReport() เสมอ

คำถาม
ปัญหาจากวิธีการนี้คืออะไรล่ะ ?
คำตอบ
การสร้างรายงานมันผูกติดกับ Employee มากเลยใช่ไหม ?

ถ้ามีการเปลี่ยนแปลง หรือ เพิ่ม หรือ ลด รูปแบบของรายงาน
เราจะต้องทำการเปลี่ยนแปลง Employee เสมอใช่ไหม ?

ลองคิดดูสิว่า Employee มีหน้าที่สร้างรายงานหรือ ?
ถ้าตอบว่า ใช่ ก็จบบทความนี้ !!

แต่ถ้าไม่ หมายความว่า Employee นั้นมีมากกว่าหนึ่งหน้าที่ใช่ไหม ?
แถมไม่ควรสร้างรายงานอีกนะ !!
นั่นคือ เราเคารพใน Single Responsibility Principle (SRP) นะ … แจ่ม

คำถาม
แล้วเราจะแก้ไขอย่างไรดีล่ะ ?
คำตอบ
ก็ใช้งานร่วมกับ composition ไงล่ะ !!
แล้วมันทำอย่างไรหว่า งงไปหมดล่ะ

วิธีการแก้ไขมีหลายวิธีการสำหรับ composition เช่น Visitor, DIP เป็นต้น
หนึ่งวิธีการที่จะแนะนำก็คือ ลองใช้ Visitor pattern กันดูไหม ?
อะไรอีกนะ เยอะเยอะจัง !!!!!!

เริ่มด้วยการสร้าง interface EmployeeVisitor ขึ้นมา

เพื่อกำหนดว่า มี Employee กี่กลุ่ม ซึ่งจากตัวอย่างมี 2 กลุ่ม
สามารถเขียน code ได้ดังนี้

ต่อมาทำการเปลี่ยนแปลงที่ Employee
โดยสร้าง method ใหม่ชื่อว่า accept()
มี argument เป็น EmployeeVisitor
เป็น abstract method เพื่อกำหนดให้ทุกๆ subclass ต้อง implement method นี้นะ
และทำการย้ายหน้าที่การสร้าง report ออกไปด้วย
สามารถเขียน code ได้ดังนี้

จากนั้น เรามาสร้าง class สำหรับออกรายงานของ HourEmployee กัน
โดยทำการสร้าง class ชื่อว่า HourReport
ทำการ implements interface EmployeeVisitor ซะ
สามารถเขียน code ได้ดังนี้

สุดท้ายก็เรียกใช้งาน ดังนี้

จะเห็นได้ว่าแต่ละ class นั้น
มีหน้าที่การทำงานอย่างใดอย่างหนึ่งไปเลย
นั่นคือการเคารพในแนวคิด Single Responsibility Principle (SRP)
และเราสามารถเพิ่มรายงานใหม่ๆ เข้าไปยัง EmployeeVisitor ได้เลย
โดยไม่กระทบต่อ Employee เลยสักนิด !!
แสดงว่ามันไม่ค่อยผูกติดกันมากนักใช่ไหมครับ ?

โดยสรุปแล้ว

ทั้งสองอย่างมันสามารถทำงานร่วมกันได้ดีนะครับ
แต่แน่นอนว่ายังแนะนำให้เน้นไปที่ composition ก่อน
และนำ inheritance มาให้งานร่วมกัน

Composition over Inheritance นะ
ไม่ใช่ Composition not Inheritance
ไม่เช่นนั้น จะเข้าใจผิดกันได้ …

Let’s code, not war …