𝗔𝗯𝘀𝘁𝗿𝗮𝗰𝘁𝗶𝗼𝗻 𝗶𝗻 𝗢𝗢𝗣: การซ่อนความซับซ้อน
คุณขับรถด้วยการกระทำง่ายๆ เพียงไม่กี่อย่าง คุณเหยียบคันเร่ง คุณหมุนพวงมาลัย คุณเหยียบเบรก
คุณไม่จำเป็นต้องรู้ว่าน้ำมันเข้าสู่เครื่องยนต์ได้อย่างไร คุณไม่จำเป็นต้องเข้าใจว่าลูกสูบเคลื่อนที่อย่างไรหรือเกียร์เปลี่ยนอย่างไร รถยนต์ได้ซ่อนรายละเอียดเหล่านั้นไว้จากคุณ คุณเพียงแค่ใช้ interface ที่เรียบง่ายเพื่อควบคุมเครื่องจักรที่ซับซ้อน
ซอฟต์แวร์ก็ทำงานในลักษณะเดียวกัน
ลองนึกถึงการส่งอีเมล คุณเรียกใช้คำสั่งเพียงคำสั่งเดียว: emailService.send()
เบื้องหลังนั้นมีหลายอย่างเกิดขึ้น:
- ระบบตรวจสอบความถูกต้องของที่อยู่
- สร้างการเชื่อมต่อ SMTP
- ทำการยืนยันตัวตนกับผู้ให้บริการ
- สร้างข้อความ
- จัดการการลองใหม่หากการพยายามครั้งแรกล้มเหลว
คุณไม่จำเป็นต้องเห็นขั้นตอนเหล่านี้ คุณแค่ต้องการให้แน่ใจว่าอีเมลถูกส่งออกไป
หากคุณไม่ใช้ abstraction โค้ดของคุณจะมีลักษณะดังนี้:
connect()authenticate()buildMessage()sendMessage()disconnect()
การทำแบบนี้ทุกครั้งจะสร้างปัญหา เพราะโค้ดทุกส่วนของคุณจะรับรู้ข้อมูลมากเกินไป หากคุณเปลี่ยนผู้ให้บริการอีเมล คุณจะต้องตามไปอัปเดตโค้ดทุกบรรทัดที่ทำหน้าที่ส่งเมล ทำให้ระบบของคุณแก้ไขได้ยาก
Abstraction เข้ามาแก้ปัญหานี้ โดยการแสดงให้เห็นว่าออบเจกต์ทำอะไรได้บ้าง แต่ซ่อนวิธีการทำงานของมันไว้
คุณเปิดเผย interface ที่เรียบง่าย ผู้เรียกใช้งาน (caller) ก็สามารถโฟกัสไปที่งานที่ต้องการทำได้ ส่วนการทำงานภายใน (implementation) ก็จะถูกซ่อนไว้ สิ่งนี้ช่วยให้โค้ดของคุณมีความเป็นอิสระต่อกัน (decoupled) และดูแลรักษาได้ง่าย
ผู้คนมักสับสนระหว่าง Abstraction กับ Encapsulation
Encapsulation ถามว่า: ใครสามารถเปลี่ยนข้อมูลนี้ได้บ้าง? มันทำหน้าที่ปกป้องสถานะภายใน (internal state) Abstraction ถามว่า: รายละเอียดอะไรบ้างที่ผู้ใช้จำเป็นต้องเห็น? มันทำหน้าที่ซ่อนความซับซ้อน
ทั้งสองอย่างทำงานร่วมกันเพื่อสร้างระบบที่ดีขึ้น
ต่อไป เราจะไปดูเรื่อง Inheritance เราจะตอบคำถามหนึ่งว่า: หากออบเจกต์มีพฤติกรรมที่เหมือนกัน คุณจำเป็นต้องเขียนโค้ดเดิมซ้ำสองครั้งหรือไม่?