สวัสดีครับสำหรับบทความนี้เราจะมาอยู่ในเรื่องของพื้นฐานสำหรับการเขียนโปรแกรม ที่สายเดฟอย่างพวกเราไม่รู้ไม่ได้ นั่นก็คือการเขียนโปรแกรมที่เราเรียกว่า OOP ชื่อเต็มว่า Object-Oriented Programming หรือการเขียนโปรแกรมเชิงวัตถุนั่นเอง
การเขียนโปรแกรมเชิงวัตถุคืออะไร?
เอาแบบสั้น ๆ มันคือแนวคิดการเขียนโปรแกรมที่เราจะเน้นไปที่การใช้ “วัตถุ” (Object) ตามชื่อเลยครับ แล้วเขียนแบบนี้ไปทำไม เพราะเมื่อเขียนโปรแกรมไปสักพักโปรแกรมซับซ้อน การจัดการ การแก้โค้ดก็จะทำได้ยากขึ้น เราเลยต้องเอา OOP มาใช้เพื่อสร้างโค้ดของเราให้สามารถจัดการง่ายขึ้น รองรับการโตไปเป็นแอปที่มีความซับซ้อน ทำได้หลายอย่างขึ้น
โดยหากเราเราพูดถึง OOP แล้วมันจะมี 4 เรื่องหลัก ๆ ที่เราจะต้องรู้จักคือ Encapsulation, Inheritance, Polymorphism และ Abstraction ซึ่งของพวกนี้ ทีมงาน BorntoDev ของเราก็ได้อธิบายไว้แบบละเอียดยิบแล้วในบทความนี้ 👉🏻 www.borntodev.com/2020/04/14/oop-จากตัวอย่างที่ง่ายๆ/
ซึ่งในบทความนี้เดี๋ยวแอดจะมาดูว่าการใช้งาน การเขียนโค้ด ท่าทางหน้าตาของโค้ดจะเป็นยังไงกัน โดยในบทความนี้เราจะใช้ตัวอย่างจากภาษา JavaScript เป็นหลัก โดยเจ้า JS ของเราเป็นภาษาที่สามารถเขียนได้หลายแบบหลายแนวทางหรือที่เรียกว่า multi-paradigm ซึ่งเราจะเลือกแนวทางที่เราเลือกมานั่นคือ OOP
Abstraction
Abstraction เสาหลักต้นแรกของ OOP ที่จะทำหน้าที่ซ่อนความซับซ้อนเอาไว้ รายละเอียดต่าง ๆ ที่เราไม่จำเป็นต้องรู้ เหลือเอาไว้แค่ข้อมูลหรือชื่อ method ที่สำคัญกับการใช้งานก็พอ ยกตัวอย่างง่าย ๆ จากสิ่งที่เราใช้อยู่ก็คือมือถือ เราสามารถใช้มือถือ หรือ คอมพิวเตอร์ อ่านบทความนี้ โดยไม่จำเป็นต้องรู้ด้วยซ้ำว่าเครื่องนี้วงจรเป็นยังไง ฮาร์แวร์ตัวไหนต่อกับตัวไหนอยู่ สิ่งที่เราจะต้องรู้ก็แค่วิธีการใช้มัน และนี่แหละคือการซ่อนรายละเอียดที่ซับซ้อนที่เราเรียกว่า Abstraction นั่นเอง! เราสามารถเอาแนวคิดนี้มาจัดการซ่อนโค้ดการทำงานของฟังก์ชันต่าง ๆ ที่มีรายละเอียดยิบย่อย ซับซ้อนเต็มไปหมด
class Car {
constructor(model, year) {
this.model = model;
this.year = year;
}
startEngine() {
console.log(`${this.model} engine started.`);
}
drive() {
this.startEngine();
console.log(`${this.model} is now driving.`);
}
}
// ผู้ใช้งานต้องการใช้งานรถ
const myCar = new Car("Toyota", 2020);
// ผู้ใช้งานเพียงแค่สั่งให้รถขับ
myCar.drive();
// Output:
// Toyota engine started.
// Toyota is now driving.
JavaScriptจากโค้ดนี้เราจะเห็นได้ว่าได้มีการซ่อนรายละเอียดที่ไม่จำเป็นแล้ โดย Class Car จะแสดงให้เห็นว่าเป็น Abstraction เนื่องจากคลาส Car ที่มี property ที่เป็น รุ่น (model) และปี (year)ส่วนที่เป็นการทำงานต่าง ๆ ของรถยนต์ (เช่นการสตาร์ทเครื่องยนต์ startEngine() และการขับรถ drive() ถูกซ่อนอยู่ในภายในคลาส ตอนที่เรียกใช้ที่มีการใช้ myCar.drive() เราไม่จำเป็นต้องรู้ว่า startEngine() ทำงานยังไงเลย เพราะในตอนที่เรียก drive() มีการสั่ง startEngine() ให้ทำงานอัตโนมัติและถูกซ่อนไว้ เราไม่จำเป็นต้องรู้ว่ามันมีอยู่ตอนที่เรียกใช้ method drive() เลย
Encapsulation (การปกปิดข้อมูล)
ต่อมาเป็นเสาหลักต้นที่สอง Encapsulation เสาที่มีหน้าที่ควบคุมการเข้าถึงข้อมูลหรือฟังก์ชันภายในคลาส โดยข้อมูลและฟังก์ชันที่เป็นส่วนประกอบของคลาสจะถูกเก็บไว้เป็นส่วนตัว (private) และสามารถเข้าถึงได้ผ่าน method ที่เปิดให้เข้าถึง (public) เท่านั้น โดยการใช้ Encapsulation หรือ การปกปิดข้อมูล มันจะช่วยเพิ่มความปลอดภัยและช่วยควบคุมวิธีการที่ข้อมูลถูกใช้ได้ดีขึ้น
ตัวอย่างเช่น เรามีคลาส BankAccount ที่มี property คือยอดเงินในบัญชี (balance) ซึ่งเราจะป้องกันไม่ให้ผู้ใช้สามารถแก้ไขยอดเงินโดยตรงได้ แต่จะต้องแก้ไขผ่าน method ที่เรากำหนดเท่านั้น เช่น deposit() และ withdraw()
class BankAccount {
constructor(balance) {
let _balance = balance; // ข้อมูลถูกซ่อนไว้
this.deposit = function(amount) {
if(amount > 0) {
_balance += amount;
console.log(`Deposited: $${amount}. New Balance: $${_balance}`);
} else {
console.log("Deposit amount must be positive!");
}
};
this.withdraw = function(amount) {
if(amount > 0 && amount <= _balance) {
_balance -= amount;
console.log(`Withdrew: $${amount}. Remaining Balance: $${_balance}`);
} else {
console.log("Invalid withdraw amount!");
}
};
this.getBalance = function() {
return _balance;
};
}
}
// สร้างบัญชีใหม่
const myAccount = new BankAccount(1000);
// ฝากเงิน
myAccount.deposit(500);
// ถอนเงิน
myAccount.withdraw(200);
// พยายามเข้าถึง balance โดยตรง (ทำไม่ได้เพราะ balance เป็น private)
console.log(myAccount._balance); // undefined
// เข้าถึง balance ผ่าน method ที่กำหนดไว้
console.log(`Final Balance: $${myAccount.getBalance()}`);
JavaScriptจากโค้ดนี้เราจะเห็นว่าข้อมูล _balance ถูกเก็บไว้เป็น private โดยไม่ได้เปิดเผยให้เห็นหรือเข้าถึงได้โดยตรงจากภายนอก ซึ่งทำให้มั่นใจได้ว่าจะไม่มีใครสามารถแก้ไขยอดเงินในบัญชีโดยตรงได้ การแก้ไขยอดเงินจะทำได้เฉพาะผ่าน method deposit() และ withdraw() เท่านั้น
Inheritance (การสืบทอด)
ต่อมาเสาหลักที่สามจะเป็นแนวคิดที่ช่วยให้เราสามารถสร้างคลาสใหม่ที่สืบทอด property และ behaviour จากคลาสที่มีอยู่แล้วได้ เป็นเหมือนแม่ลูกที่ได้รับการสืบทอดอะไรบางอย่างมา เช่น ลูกสามารถใช้ของที่แม่มี แต่ลูกก็สามารถมีของที่เป็นของลูกเพิ่มมาได้ โดยการสืบทอดนี้มันทำให้การเขียนโปรแกรมสะดวกและประหยัดเวลาในการสร้างโค้ดที่มีความซ้ำซ้อน ยกตัวอย่างในการเขียนโค้ดเช่น เราสามารถสร้างคลาส Vehicle ที่เป็นคลาสแม่ แล้วสร้างคลาสลูกที่สืบทอดคุณสมบัติต่าง ๆ ของคลาสแม่ได้ เช่น Car หรือ Motorcycle
class Vehicle {
constructor(make, model) {
this.make = make;
this.model = model;
}
startEngine() {
console.log(`${this.make} ${this.model} engine started.`);
}
}
class Car extends Vehicle {
drive() {
console.log(`${this.make} ${this.model} is now driving.`);
}
}
// สร้างรถใหม่จากคลาส Car ที่สืบทอดมาจาก Vehicle
const myCar = new Car("Toyota", "Corolla");
// เรียกใช้ฟังก์ชันจากคลาสแม่และคลาสลูก
myCar.startEngine();
myCar.drive();
JavaScriptจากโค้ดนี้เราจะเห็นว่า Car สืบทอดฟังก์ชัน startEngine() จากคลาส Vehicle ซึ่งช่วยให้เราประหยัดเวลาในการเขียนโค้ดที่ต้องทำซ้ำ และเพิ่มความยืดหยุ่นในการจัดการโค้ดของเราได้
Polymorphism (พหุนิยม)
มาที่เสาหลักเสาสุดท้ายเป็นแนวคิดใน OOP ที่ช่วยให้เราสามารถใช้ method หรือฟังก์ชันเดียวกันกับ object ที่แตกต่างกันได้ โดยการเปลี่ยน behaviour ของฟังก์ชันตามประเภทของ object ที่ถูกเรียกใช้งาน ทำให้โปรแกรมมีความยืดหยุ่นขึ้น เช่น เรามีคลาสเป็นคลาสแม่ Animal และคลาสลูก Dog และ Cat ที่สืบทอดจากคลาสแม่ โดยทั้งสองคลาสลูกมีการ override method speak() ให้แสดงพฤติกรรมที่ต่างกัน
class Animal {
speak() {
console.log("This animal makes a sound.");
}
}
class Dog extends Animal {
speak() {
console.log("The dog barks.");
}
}
class Cat extends Animal {
speak() {
console.log("The cat meows.");
}
}
function makeAnimalSpeak(animal) {
animal.speak();
}
const myDog = new Dog();
const myCat = new Cat();
// ใช้ฟังก์ชันเดียวกัน แต่พฤติกรรมเปลี่ยนไปตามประเภทของวัตถุ
makeAnimalSpeak(myDog); // Output: The dog barks.
makeAnimalSpeak(myCat); // Output: The cat meows.
JavaScriptจากโค้ดนี้เราจะเห็นว่าฟังก์ชัน makeAnimalSpeak() สามารถใช้กับ object ประเภท Dog และ Cat ได้ ซึ่งทั้งสองมีการ override method speak() ให้ทำงานแตกต่างกันตามประเภทของสัตว์นั้น ๆ นี่คือความสามารถของ Polymorphism นั่นเอง
จากทั้งสี่ concept เราจะเห็นได้ว่าการเขียนโปรแกรมเชิงวัตถุ (OOP) นั้นเป็นแนวคิดที่สำคัญในการพัฒนาโปรแกรมที่มีความซับซ้อน ซึ่งจะช่วยให้โค้ดของเราสามารถจัดการได้ง่ายขึ้น และมีความยืดหยุ่น โดยมีเสาหลัก 4 อย่างที่สำคัญคือ Abstraction, Encapsulation, Inheritance และ Polymorphism นั้นเอง