Object-Oriented Programming (OOP) คืออะไรกันแน่?
ทุกวันนี้เราอยู่ในยุคของการเขียนโปรแกรมแบบ Object-Oriented ภาษาไหนๆก็เป็นภาษาแบบออบเจกต์แทบทั้งนั้น ไม่ว่าจะเป็น Java, Ruby, Python, PHP, JavaScript หรืออื่นๆล้วนแล้วแต่เขียนเป็นออบเจกต์ได้ทั้งนั้น (บางภาษาอาจเป็นได้มากกว่า Object-Oriented) แต่แล้วการเขียนโปรแกรมแบบ Object-Oriented มันคืออะไรกันแน่? หลายคนยังคงหาคำตอบไม่ได้ อ่านหนังสือก็แล้ว เรียนในห้องมาก็แล้ว หรือแม้แต่ทำงานเขียนโปรแกรมอยู่แล้ว ก็ยังไม่รู้ว่าจริงๆแล้วมันคืออะไร เราได้อะไรจากมัน แล้วที่เราเขียนอยู่นี้หล่ะ ใช่เขียนเป็นออบเจกต์รึเปล่า!!! ในบทความนี้ผมจะอธิบายให้คุณเห็นว่าแท้จริงแล้วมันคืออะไร มันอาจไม่ใช่ที่เราเคยเข้าใจกันมาหรือมันอาจง่ายกว่าที่เราคิดไว้ก็ได้
Spaghetti-Oriented Programming!!!
ก่อนเราจะไปหาคำตอบกันว่า OOP คืออะไร ผมอยากจะเริ่มต้นที่การเขียนแบบที่ไม่ใช่ OOP ก่อน ถึงแม้ว่าเราจะใช้ภาษาออบเจกต์อย่างเช่น Java เขียนโปรแกรม แต่นั่นก็ไม่ได้การันตีว่าเราได้เขียนมันเป็นออบเจกต์ บ่อยครั้งที่เราเขียนโปรแกรมโดยสนใจแต่ผลลัพธ์เป็นหลัก เราจะเขียนโค้ดอะไรก็ตามที่ทำให้มันรันได้และออกมาเป็นผลลัพธ์ที่ต้องการ สิ่งนี้มักเกิดขึ้นบ่อยโดยเฉพาะถ้าเราใช้ Tool/IDE ดีๆอาทิเช่น Netbeans, Visual Studio เป็นต้น IDE เหล่านี้มักช่วยให้เราทำงานได้ง่ายเสียเหลือเกิน มันสร้างโค้ดให้เราบางส่วน เราเติมเองเพิ่มบางส่วน มันก็ออกมาเป็นโปรแกรมใช้งานได้แล้ว เอาหล่ะผมจะลองยกตัวอย่างขั้นตอนการเขียนแบบที่ว่านี้ให้ดูนะครับดังนี้:
- เราเริ่มต้นที่การออกแบบ User Interface (UI) ก่อน โดยใช้ Visual Disigner ฟีเจอร์ของ IDE รูปที่ 1 แสดงตัวอย่างของ Netbeans IDE เราลากวาง Controls/Components ต่างๆตามแต่ที่เราต้องการ เดี๋ยว IDE มันก็สร้างโค้ดให้เราเอง
รูปที่ 1: Netbeans Visual Designer - จากนั้นเราก็ให้ IDE มันสร้างเมธอดสำหรับ Event-Handling ให้ เราเพียงดับเบิ้ลคลิกที่ปุ่มเช่น ปุ่ม "Save" มันก็สร้างเมธอดให้เราไว้สำหรับเขียนโค้ดเพื่อจัดการกับเหตุการณ์ที่ปุ่มโดนกดดังแสดงในรูปที่ 2
รูปที่ 2: Event-Handling Method - ถึงตรงนี้เราก็แค่เติมโค้ดอะไรก็ได้ลงไปให้มันใช้งานได้ก็จบ
แล้วจริงๆเราได้อะไรมา? คำตอบก็คือเราได้โปรแกรมแบบ Spaghetti-oriented มา เพราะเราผสมรวมมันทุกอย่างลงไปในไฟล์ๆเดียวดังแสดงในรูปที่ 3 เห็นไหมครับว่า ไม่ว่าจะเป็นโค้ดที่สร้าง UI ขึ้นมาก็อยู่ที่ไฟล์นี้, โค้ดที่ใช้จัดการกับการกดปุ่มก็ไฟล์นี้, โค้ดที่ใช้ติดต่อฐานข้อมูลก็ไฟล์นี้, . . . มันผสมปนเปยุ่งเหยิงอยู่ในไฟล์เดียวกันหรือแย่กว่านั้นคือมีข้ามไฟล์ให้มันสะเปะสะปะเข้าไปอีก แล้วถ้าเราจะแก้ไขมันในอนาคตจะทำอย่างไร คงจะปวดหัวน่าดู และถ้าเราจะ Re-use มันหล่ะจะได้มั๊ย เช่นว่าปุ่มปิดมันน่าจะมีในทุกๆหน้าจอ UI เราจะ Re-use มันยังไง? ก๊อปปี้แปะมันเลยละกันถ้างั้น ถ้าคุณทำเช่นนั้นคุณกำลังใช้แนวคิดแบบ Ctrl-c และ Ctrl-v Oriented อยู่ :p ทั้งหมดนี้แน่นอนว่าไม่ใช่การเขียนแบบ OOP เลยแม้ว่าเราจะใช้ภาษาออบเจกต์
ตกลง OOP คืออะไร อยากรู้แล้ว
แล้วตกลง OOP คืออะไรกันแน่? บางคนก็ว่ามันคือการเขียนโปรแกรมเชิงวัตถุ ก็แน่หล่ะก็นี่คือคำแปลตรงตามตัวเลยนิครับ บ้างก็ว่ามันคือ Encapsulation, Inheritance, Polymorphism . . . เออ . . .อันนี้ยิ่งไปกันใหญ่ ถามว่าถูกมั๊ย มันก็ไม่ผิดอะไร เพราะเรียน OOP ทีไรก็มีชื่อเท่ๆเหล่านี้เสมอ แต่แล้วเจ้าชื่อเรียกเท่ๆยาวๆเหล่านั้นมันคืออะไร? เราได้อะไรจากมัน? ตอบได้มั๊ยครับ? ลืมมันไปก่อนเถอะครับ มันเป็นแค่ฟีเจอร์ทางภาษาเท่านั้น เพราะถึงแม้คุณจะเขียนโค้ด Abstract, Polymorphism หรืออื่นๆ คุณก็ไม่ได้ประโยชน์อะไรจากมันถ้าคุณยังไม่เข้าใจหัวใจหลัก (BASIC PRINCIPLE) ของ OOP ยกตัวอย่างเช่น คุณใส่ฟีเจอร์ทางภาษาเข้าไป แต่โค้ดมันยังเป็นสปาเก็ตตี้อยู่ก็เท่านั้น ถูกมั๊ยครับ เจ้าฟีเจอร์ทางภาษามันแค่ช่วยเสริมหัวใจหลักให้ดียิ่งขึ้น
หัวใจหลักของ OOP คือ การแบ่งโปรแกรมหรือแอพพลิเคชันออกเป็นโมดูล(ส่วน)ย่อยๆ แต่ละโมดูลมีหน้าที่การทำงานหลักเพียงอย่างเดียวเท่านั้น และโมดูลทั้งหมดเหล่านั้นทำงานร่วมกันเป็นแอพลิเคชันที่สมบูรณ์ คุณยังไม่เข้าใจมันหรอกถ้ายังไม่เห็นตัวอย่าง งั้นเรามาดูกันเลยดีกว่า สมมุติผมจะสร้างแอพพลิเคชันขึ้นมาสักอันหนึ่ง ผมอาจแบ่งแอพพลิเคชันออกเป็นโมดูลย่อยๆดังแสดงในรูปที่ 4
แต่ละโมดูลจะมีหน้าที่หลักหรือบทบาท (Role) หลักเพียงอย่างเดียวเท่านั้น เช่น:
- โมดูล GUI จะมีแต่โค้ดที่เกี่ยวข้องกับการแสดงผล (Presentation Logic) เท่านั้น จะไม่มีอย่างอื่นเลย แต่แล้วข้อมูลที่จะนำมาแสดงผลจะมาจากไหนหล่ะ ผมก็ให้มันเป็นหน้าที่ของโมดูล Service
- โมดูล Service มีหน้าที่ในการประมวลผล มันประกอบไปด้วย Business Logic ต่างๆ เช่นการคำนวณราคา, การคิดภาษี เป็นต้น เพื่อเอาผลลัพธ์ไปใช้ในโมดูล GUI แต่การจะประมวลผลได้นั้นมันต้องมีข้อมูลดิบป้อนเข้ามา ซึ่งข้อมูลดิบมักถูกเก็บอยู่ในระบบฐานข้อมูล อย่างไรก็ตามนี่ไม่ใช่หน้าที่หรือบทบาทของโมดูล Service แต่เป็นหน้าที่ของโมดูลต่อไปโมดูล DAO
- โมดูล DAO (Data Access Object) ชื่อก็บอกอยู่แล้วว่าเป็นโมดูลที่ใช้เข้าถึงข้อมูล มีหน้าที่ในการนำข้อมูลเข้าออกจากระบบฐานข้อมูลหรือประกอบไปด้วยโค้ด Data Access Logic เท่านั้น ส่วนจะประมวลผลข้อมูลอย่างไรก็เป็นหน้าที่ของ Service
- โมดูล Report คงเดาได้ไม่ยากว่า มีหน้าที่ออกรายงานอย่างเดียว ก็เผอิญว่าแอพพลิเคชันนี้มีการออกรายงานด้วย
- สุดท้ายโมดูล Main มีหน้าที่ในการโหลดหรือประกอบร่างโมดูลต่างๆเข้าด้วยกันเพื่อให้ได้ออกมาเป็นแอพพลิเคชันที่ใช้งานได้
เห็นไหมครับว่าแต่ละส่วนไม่มีการก้าวก่ายงานกัน แล้วเราได้อะไรจากการออกแบบเช่นนี้? คำตอบคือเราสามารถเปลี่ยนแปลง, แก้ไข, เพิ่มเติมได้อย่างอิสระ ยกตัวอย่างเช่น
- หากเรามีการเปลี่ยนแปลงระบบฐานข้อมูล เช่นจะเปลี่ยนโครงสร้างตารางหากใช้ Relational Database หรือแม้แต่จะเปลี่ยนจาก Relational ไปเป็น Object Database เราก็มั่นใจได้ว่าโค้ดที่ต้องเปลี่ยนแปลงอยู่ในโมดูล DAO เท่านั้น ก็การแสดงผลก็เหมือนเดิม, Business Logic ก็เหมือนเดิม, ออกรายงานก็อย่างเดิมจะไปแก้อย่างอื่นมันทำไม
- หากเราอยากปรับปรุงหน้าตาการแสดงผล (GUI) ให้มันดูดีกว่าที่เป็นอยู่ เราก็แก้ไขที่โมดูล GUI เท่านั้น ก็ฐานข้อมูลมันอย่างเดิม, อย่างอื่นก็เหมือนเดิม จะแก้อย่างอื่นไปทำไม
- เอาหล่ะวันนี้มันยังเป็น Desktop App (Window App) พรุ่งนี้จะทำให้มันเป็น Web App เราต้องรื้อทำใหม่หมดรึเปล่า? เปล่าเลย ก็ฐานข้อมูลมันก็ชุดเดิมไม่ใช่หรือ, Business Logic ก็ไม่ได้เปลี่ยนไป แค่อยากไปแสดงผลบนเว็บ ถ้างั้นก็เพิ่มโมดูลเว็บเข้าไปสิ อย่างอื่นที่เหลือก็ Re-use :)
- วันนี้เราเขียนด้วย Java พรุ่งนี้แอพพลิคชันมันต้องทำงานร่วมกับแอพพลิเคชันอื่นที่ดันเขียนด้วย .Net เราจะทำอย่างไรดี? เราก็เพิ่มโมดูล Web Service เข้าไปสิ เพราะเทคโนโลยี Web Services มันเกิดมาเพื่อการนี้คือทำให้ต่างภาษาต่างแพลทฟอร์มทำงานร่วมกันได้
พอจะเห็นภาพหรือยังครับว่าถ้าเราแบ่งแอพพลิเคชันได้ดีคือแบ่งออกเป็นสัดเป็นส่วน เราก็สามารถเปลี่ยนแปลง, แก้ไข, เพิ่มเติมได้ง่ายขึ้น ได้ประโยชน์แม้ไม่มีโค้ดไฮไซอย่าง Abstract, Polymorphism, Overriding ใดๆทั้งสิ้น แค่นี้แหละครับหัวใจหลักของ OOP ส่วนจะแบ่งอย่างไรให้มันดีหรือเหมาะสม อันนี้ก็ต้องไปว่ากันที่เรื่องของการออกแบบกันต่อไป
กลับมาต่อกันที่หลักการของ OOP กัน ในทางปฏิบัติแล้วแต่ละโมดูลก็คือออบเจกต์นั่นเอง ดังนั้นการเขียนโปรแกรมแบบ Object-Oriented ก็คือ การแบ่งโปรแกรมหรือแอพพลิเคชันออกเป็นออบเจกต์ย่อยๆ แต่ละออบเจกต์ทำหน้าที่หลักเพียงอย่างเดียวหรือมีเพียงบทบาทเดียว สุดท้ายทุกๆออบเจกต์ทำงานร่วมกันออกมาเป็นแอพพลิเคชันที่สมบูรณ์ ในความเป็นจริงแต่ละบทบาทอาจประกอบไปด้วยออบเจกต์มากกว่าหนึ่งตัวก็ได้เช่น บทบาทการแสดงผล (GUI) อาจต้องใช้ออบเจกต์มากกว่าหนึ่งตัว เพราะหนึ่งออบเจกต์ก็หนึ่งหน้าจอ เป็นต้น ดังนั้นออบเจกต์ต่างๆที่อยู่ในบทบาทเดียวกัน ก็น่าจะถูกจัดให้อยู่ในกลุ่มเดียวกันดังแสดงในรูปที่ 5 กลุ่มของออบเจกต์ก็คือ Package หรือ Namespace หรือ Library หรือ Component สุดแท้แล้วแต่จะเรียกนั่นเอง เพราะฉะนั้นหากผมอยากจะเปลี่ยนหน้าการแสดงผลใหม่ ผมก็โละมันทั้ง GUI Package ได้เลย จะเห็นได้ว่าหัวใจหลักของ OOP คือการจัดแบ่งโค้ดอย่างมีระเบียบ พูดง่ายๆคืออย่าเขียนเป็นสปาเก็ตตี้ก็แล้วกัน
แล้วที่เราร่ำเรียนกันมาเกี่ยวกับ Inheritance, Polymorphism ทั้งหลายทั้งปวงมันคืออะไร นั่นเป็นฟีเจอร์ทางภาษาของ Object-Oriented ครับ ในเมื่อแอพพลิเคชันถูกแบ่งออกเป็นออบเจกต์ย่อยๆมากมาย (ในระบบใหญ่ๆมีได้เป็นหลักร้อยหลักพัน) และมันจะต้องทำงานร่วมกัน ฟีเจอร์ทางภาษาเหล่านั้นจะทำให้มันทำงานร่วมกันได้ดียิ่งขึ้น เพราะฉะนั้นจุดเริ่มต้นคือต้องแบ่งออบเจกต์ให้ดีเสียก่อน แล้วจึงใส่ฟีเจอร์ทางภาษาเข้าไป
What's Next?
น่าจะพอเห็นภาพแล้วนะครับว่าแนวคิดแบบ OOP เป็นอย่างไร ต่อจากนี้ไปคุณก็ไปศึกษาว่าในภาษานั้นๆมีวิธีการสร้างออบเจกต์อย่างไรใช้ Syntax แบบไหน แล้วจึงศึกษาฟีเจอร์ทางภาษา Object-Oriented ที่ทำให้ออบเจกต์ทำงานร่วมกันอย่างมีประสิทธิภาพอาทิเช่น Polymorphism เป็นต้น ในโอกาสถัดไปผมจะเขียนถึงออบเจกต์ใน Java หน้าตาเป็นอย่างไร? Hello World แบบ OOP มันควรเป็นเช่นไรกัน? และชื่อเท่ๆอย่าง Encapsulation, Information Hiding, และ Polymorphism แท้จริงแล้วมันคืออะไรกันแน่ ขอให้สนุกกับการเขียนโปรแกรมแบบ OOP นะครับ :)
Update - บทที่ความต่อเนื่อง: