SOLID 5 ข้อ
👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ
หลักการออกแบบ class/module 5 ข้อที่ทำให้โค้ดยืดหยุ่น แก้ง่าย และทดสอบง่าย
SOLID คือชุดหลักการออกแบบ 5 ข้อสำหรับเขียนโค้ดเชิงวัตถุ (OOP) ที่ดูแลและต่อยอดง่าย ไม่ต้องท่องตายตัว แต่เข้าใจแนวคิดแล้วจะออกแบบโค้ดได้ดีขึ้น
S — Single Responsibility
แต่ละ class ควรมีเหตุผลให้เปลี่ยนแค่เรื่องเดียว (รับผิดชอบเรื่องเดียว)
python
# ❌ class เดียวทำทั้งคำนวณและบันทึกไฟล์ (2 เหตุผลที่จะเปลี่ยน)
class Report:
def calculate(self): ...
def save_to_file(self): ...
# ✅ แยกความรับผิดชอบ
class Report:
def calculate(self): ...
class ReportSaver:
def save(self, report): ...O — Open/Closed
เปิดให้ "ขยาย" (เพิ่มพฤติกรรมใหม่) แต่ปิดการ "แก้ของเดิม" — เพิ่มฟีเจอร์โดยไม่ต้องไปรื้อโค้ดที่ทำงานอยู่
python
# ✅ เพิ่มรูปร่างใหม่ได้โดยไม่แก้โค้ดเดิม
class Shape:
def area(self): raise NotImplementedError
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return 3.14159 * self.r ** 2
class Square(Shape):
def __init__(self, s): self.s = s
def area(self): return self.s ** 2
def total_area(shapes): # ไม่ต้องแก้เมื่อเพิ่มรูปใหม่
return sum(s.area() for s in shapes)L, I, D โดยย่อ
| ตัวอักษร | หลักการ | ใจความ |
|---|---|---|
| L | Liskov Substitution | subclass ต้องใช้แทน parent ได้โดยไม่พัง |
| I | Interface Segregation | อย่าบังคับให้ implement method ที่ไม่ใช้ |
| D | Dependency Inversion | พึ่ง abstraction ไม่ใช่ implementation ตายตัว |
python
# D — Dependency Inversion: รับ dependency เข้ามา (ไม่สร้างเองตายตัว)
# ❌ ผูกกับ implementation
class Service:
def __init__(self):
self.db = MySQLDatabase() # ผูกตาย
# ✅ รับเข้ามา (inject) — สลับ/mock ได้ง่าย
class Service:
def __init__(self, db):
self.db = db # จะเป็น MySQL หรือ mock ก็ได้SOLID เป็นแนวทาง ไม่ใช่กฎตายตัว
อย่ายึด SOLID จนทำโค้ดซับซ้อนเกินจำเป็น (ละเมิด KISS/YAGNI) ใช้เมื่อมันแก้ปัญหาจริง โดยเฉพาะ Dependency Inversion ช่วยให้ mock ในเทสต์ได้ง่าย (เชื่อมบท Testing)
สรุปหัวข้อนี้
- S: class รับผิดชอบเรื่องเดียว
- O: เพิ่มพฤติกรรมใหม่ได้โดยไม่แก้ของเดิม
- L: subclass แทน parent ได้; I: อย่าบังคับ method ที่ไม่ใช้
- D: พึ่ง abstraction รับ dependency เข้ามา — ทำให้ mock/สลับง่าย
แบบฝึกหัด
1) หา class ที่ทำหลายหน้าที่แล้วแยกตาม SRP 2) ออกแบบ Shape ที่เพิ่มรูปใหม่ได้โดยไม่แก้ total_area (OCP) 3) แก้ class ที่สร้าง db เองให้รับเข้ามาแทน (DIP) 4) อธิบายว่า DIP ช่วยการเขียนเทสต์อย่างไร