เขียนโค้ดด้วยหลักการ SOLID ด้วย JavaScript
“SOLID คือชุดของหลักการในการออกแบบซอฟต์แวร์ เพื่อให้โค้ดมีโครงสร้างที่ดี ยืดหยุ่น และดูแลรักษาได้ง่าย โดยเฉพาะเมื่อโปรเจกต์เติบโตขึ้น”
SOLID ย่อมาจาก
- S – Single Responsibility Principle
- O – Open/Closed Principle
- L – Liskov Substitution Principle
- I – Interface Segregation Principle
- D – Dependency Inversion Principle
1. Single Responsibility Principle (SRP)
“คลาส (หรือฟังก์ชัน) ควรมีเหตุผลในการเปลี่ยนแปลงเพียงอย่างเดียว”
❌ ไม่ดี:
class User {
saveToDatabase() { ... }
sendEmail() { ... }
}
✅ ดี:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
class UserRepository {
save(user) { ... }
}
class EmailService {
sendWelcomeEmail(user) { ... }
}
แยกความรับผิดชอบ: User เป็นข้อมูล, UserRepository จัดการฐานข้อมูล, EmailService ส่งอีเมล
2. Open/Closed Principle (OCP)
“เปิดสำหรับการขยาย แต่ปิดสำหรับการแก้ไข”
❌ ไม่ดี:
function getDiscount(type) {
if (type === "gold") return 0.2;
if (type === "silver") return 0.1;
return 0;
}
✅ ดี:
class Discount {
getRate() {
return 0;
}
}
class GoldDiscount extends Discount {
getRate() {
return 0.2;
}
}
class SilverDiscount extends Discount {
getRate() {
return 0.1;
}
}
เราสามารถเพิ่ม PlatinumDiscount ได้โดยไม่ต้องแก้โค้ดเดิม
3. Liskov Substitution Principle (LSP)
“สามารถแทนที่คลาสแม่ด้วยคลาสลูกได้ โดยไม่ทำให้โปรแกรมพัง”
❌ ไม่ดี:
class Bird {
fly() {
console.log("flying");
}
}
class Ostrich extends Bird {
fly() {
throw new Error("I can't fly");
}
}
✅ ดี:
class Bird {}
class FlyingBird extends Bird {
fly() {
console.log("flying");
}
}
class Ostrich extends Bird {
// ไม่สืบทอดฟังก์ชันที่ทำไม่ได้
}
อย่าสืบทอดพฤติกรรมที่คลาสลูกทำไม่ได้
4. Interface Segregation Principle (ISP)
“อย่าบังคับให้คลาสลูกต้องใช้ interface ที่มันไม่ต้องการ” (ใน JavaScript ไม่มี interface แบบใน TypeScript หรือ Java, แต่แนวคิดยังใช้ได้)
❌ ไม่ดี:
class Machine {
print() {}
scan() {}
fax() {}
}
class PrinterOnly extends Machine {
scan() {
throw new Error("Not supported");
}
fax() {
throw new Error("Not supported");
}
}
✅ ดี:
class Printer {
print() {}
}
class Scanner {
scan() {}
}
แยก interface ตามความสามารถจริง
5. Dependency Inversion Principle (DIP)
“พึ่งพา abstraction ไม่ใช่ implementation”
❌ ไม่ดี:
class MySQLDatabase {
save(data) { ... }
}
class UserService {
constructor() {
this.db = new MySQLDatabase();
}
}
✅ ดี:
class UserService {
constructor(database) {
this.database = database;
}
saveUser(user) {
this.database.save(user);
}
}
// ใช้งาน
const db = new MySQLDatabase();
const userService = new UserService(db);
ทำให้เปลี่ยน database เป็น MongoDB หรือ Mock สำหรับเทสต์ได้ง่าย
📌 สรุป
หลักการ | คำอธิบาย |
---|---|
Single Responsibility Principle (SRP) | คลาสควรมีเหตุผลในการเปลี่ยนแปลงเพียงอย่างเดียว |
Open/Closed Principle (OCP) | เปิดสำหรับการขยาย แต่ปิดสำหรับการแก้ไข |
Liskov Substitution Principle (LSP) | สามารถแทนที่คลาสแม่ด้วยคลาสลูกได้ โดยไม่ทำให้โปรแกรมพัง |
Interface Segregation Principle (ISP) | อย่าบังคับให้คลาสลูกต้องใช้ interface ที่มันไม่ต้องการ |
Dependency Inversion Principle (DIP) | พึ่งพา abstraction ไม่ใช่ implementation |