นักพัฒนา C ถกเถียงเรื่องรูปแบบการเขียนโปรแกรมเชิงวัตถุในการพัฒนา Kernel

ทีมชุมชน BigGo
นักพัฒนา C ถกเถียงเรื่องรูปแบบการเขียนโปรแกรมเชิงวัตถุในการพัฒนา Kernel

การอภิปรายล่าสุดเกี่ยวกับการนำรูปแบบการออกแบบเชิงวัตถุมาใช้ใน C ได้จุดประกายการถกเถียงอย่างเข้มข้นในหมู่นักพัฒนา โดยเฉพาะอย่างยิ่งในแวดวงการพัฒนา kernel การสนทนามุ่งเน้นไปที่การใช้ function pointer ในโครงสร้างเพื่อให้เกิด polymorphism ใน C ซึ่งเป็นเทคนิคที่มีมาก่อนภาษาโปรแกรมเชิงวัตถุแบบดั้งเดิม แต่มีเป้าหมายที่คล้ายคลึงกัน

แนวทาง Vtable เทียบกับ OOP แบบดั้งเดิม

เทคนิคหลักเกี่ยวข้องกับการสร้าง vtables - โครงสร้างที่มี function pointer ซึ่งทำหน้าที่เป็น interface สำหรับ object type ต่างๆ วิธีนี้ช่วยให้นักพัฒนาสามารถเขียนโค้ดที่อุปกรณ์หรือบริการต่างๆ สามารถใช้ API เดียวกันได้ ในขณะที่มีการทำงานภายในที่แตกต่างกันอย่างมาก อย่างไรก็ตาม สมาชิกในชุมชนแบ่งออกเป็นสองฝ่ายว่าแนวทางนี้ถือเป็นการเขียนโปรแกรมเชิงวัตถุจริงๆ หรือเป็นสิ่งที่แตกต่างไปจากเดิมโดยพื้นฐาน

นักพัฒนาบางคนโต้แย้งว่ารูปแบบนี้เป็นการแยกข้อมูลออกจากกัน (data abstraction) มากกว่าที่จะเป็น OOP จริงๆ โดยชี้ให้เห็นความแตกต่างสำคัญจากภาษาอย่าง C++ และ Java ซึ่งแตกต่างจาก OOP แบบดั้งเดิม แนวทางใน C นี้อนุญาตให้มี function ที่ไม่ได้ implement (NULL pointer) และไม่บังคับใช้ contract เหมือนที่ภาษาเชิงวัตถุทั่วไปต้องการ Linux kernel ใช้รูปแบบนี้อย่างกว้างขวางในโครงสร้างอย่าง file_operations แม้ว่านักวิจารณ์จะสังเกตว่าสิ่งเหล่านี้มี function pointer ที่ปกติจะไม่ปรากฏใน vtable จริงๆ

ความแตกต่างหลัก: Data Abstraction เทียบกับ OOP

  • Data Abstraction (รูปแบบ C): อนุญาตให้มี function pointer แบบ NULL ไม่มีข้อตกลงการสืบทอด การส่งผ่าน object แบบชัดเจน
  • Traditional OOP: บังคับใช้ข้อตกลงการดำเนินการ มี this pointer แบบโดยนัย ลำดับชั้นการสืบทอด
  • ประสิทธิภาพ: vtable ของ C สามารถเปลี่ยนแปลงได้ขณะรันไทม์เพื่อการเปลี่ยนแปลงพฤติกรรมแบบไดนามิก

ความท้าทายด้าน Syntax และข้อกังวลเชิงปฏิบัติ

ประเด็นสำคัญที่เป็นที่ถกเถียงกันหมุนรอบความต้องการ syntax ของแนวทางนี้ รูปแบบนี้มักส่งผลให้เกิดโค้ดที่ยาวเหยียด ซึ่งนักพัฒนาต้องส่ง object reference อย่างชัดเจน ทำให้เกิดการเรียกใช้แบบ object->ops->start(object) ความซ้ำซ้อนนี้ทำให้นักพัฒนาหลายคนรู้สึกหงุดหงิด ซึ่งพวกเขาชอบพฤติกรรม this pointer แบบโดยนัยที่พบในภาษา OOP แบบดั้งเดิมมากกว่า

การต้องส่ง object อย่างชัดเจนทุกครั้งรู้สึกไม่คล่องตัว โดยเฉพาะเมื่อเปรียบเทียบกับ C++ ที่ this เป็นแบบโดยนัย

อย่างไรก็ตาม ผู้สนับสนุนโต้แย้งว่าแนวทางที่ชัดเจนนี้จริงๆ แล้วช่วยปรับปรุงความชัดเจนของโค้ดโดยทำให้ dependency และความสัมพันธ์ของ object โปร่งใสมากขึ้น ซึ่งเป็นคุณลักษณะที่มีค่าในการเขียนโปรแกรมระดับ kernel ที่การเข้าใจการไหลของข้อมูลเป็นสิ่งสำคัญ

โครงสร้างพื้นฐานของรูปแบบ Vtable:

/*Interface ที่มี function pointers*/
struct device_ops {
    void (*start)(void);
    void (*stop)(void);
};

/*Device struct ที่เก็บ pointer ไปยัง ops*/
struct device {
    const char *name;
    const struct device_ops*ops;
};

วิธีแก้ไขทางเลือกและการพัฒนาภาษา

การอภิปรายได้เน้นย้ำถึงวิธีแก้ไขปัญหาและแนวทางทางเลือกต่างๆ นักพัฒนาบางคนแนะนำให้ใช้ macro magic เพื่อลดความซับซ้อนของ syntax ที่ยาวเหยียด ในขณะที่คนอื่นๆ ชี้ไปที่ framework ที่มีอยู่แล้วอย่าง GLib หรือเครื่องมือเฉพาะทางอย่างโปรเจ็กต์ co2 ที่ให้รูปแบบเชิงวัตถุที่สวยงามกว่าใน C

การถกเถียงยังสัมผัสถึงเหตุผลที่รูปแบบเหล่านี้ไม่ได้ถูกรวมเข้าไปในมาตรฐาน C ใหม่ๆ อย่างเป็นทางการ แม้จะมีการใช้งานอย่างแพร่หลาย สมาชิกในชุมชนสังเกตว่าปรัชญาของ C เน้นการทำให้ความซับซ้อนมองเห็นได้มากกว่าการซ่อนไว้หลัง syntactic sugar ซึ่งมีอิทธิพลต่อนักพัฒนาให้ใช้ dynamic dispatch เฉพาะเมื่อจำเป็นจริงๆ เท่านั้น

ตัวอย่างจาก Linux Kernel:

  • struct file_operations - ชั้นนามธรรมของระบบไฟล์
  • struct inode_operations - ฟังก์ชันสำหรับจัดการ inode
  • โครงสร้างการจัดการบริการสำหรับ kernel threads
  • การใช้งานนโยบายของ scheduler ที่มีการดำเนินการ yield, block, add, next

ประโยชน์ด้านประสิทธิภาพและความยืดหยุ่น

แม้จะมีข้อกังวลเรื่อง syntax แต่นักพัฒนาหลายคนชื่นชมความยืดหยุ่นในการทำงานที่แนวทางนี้ให้ Vtable สามารถเปลี่ยนแปลงได้ระหว่างการทำงานของโปรแกรม ทำให้สามารถเปลี่ยนพฤติกรรมแบบไดนามิกได้โดยไม่ต้องแก้ไขโค้ดที่เรียกใช้ ความสามารถนี้มีประโยชน์อย่างยิ่งใน kernel module และระบบฝังตัวที่ความสามารถในการปรับตัวขณะทำงานเป็นสิ่งสำคัญ

รูปแบบนี้ยังทำงานร่วมกับระบบ kernel module ได้ดี ช่วยให้สามารถโหลด driver หรือ hook ที่กำหนดเองแบบไดนามิกได้โดยการเปลี่ยน vtable ในโครงสร้างที่มีอยู่ ความสามารถในการขยายนี้ช่วยให้สามารถแก้ไข kernel ได้โดยไม่ต้อง compile ใหม่หรือ reboot ระบบ ทำให้น่าสนใจสำหรับการพัฒนาระดับระบบ

การถกเถียงที่ดำเนินอยู่สะท้อนถึงคำถามที่กว้างขึ้นเกี่ยวกับการพัฒนา paradigm การเขียนโปรแกรมและการแลกเปลี่ยนระหว่างความเรียบง่ายของภาษาและความสะดวกสบายของนักพัฒนา ในขณะที่บางคนสนับสนุนการย้ายไปใช้ภาษาที่มีการสนับสนุน OOP โดยธรรมชาติ คนอื่นๆ ให้ความสำคัญกับแนวทางที่ชัดเจนของ C และการควบคุมที่ให้เหนือทรัพยากรระบบและพฤติกรรม

อ้างอิง: Object-oriented design patterns