มาลองทำความรู้จัก SOLID — หลักการออกแบบในการพัฒนา Software
Photo by Med Badr Chemmaoui on Unsplash
SOLID
คือ คำย่อของชุดหลักการออกแบบ 5 ประการ (S.O.L.I.D) หลักการเหล่านี้ช่วยให้ Software developer สามารถออกแบบระบบซอฟต์แวร์เชิงวัตถุได้อย่างมีระบบ (Object-Oriented Software systems) สามารถทดสอบได้(testable), ขยายได้(extensible) และบำรุงรักษาได้(maintainable)
หลักการ SOLID ถูกคิดค้นโดย Robert C.Marin
หรือที่หลายๆคนรู้จักในนาม “Uncle Bob”
- S — Single-Responsibility principle (SRP)
- O — Open/Closed principle (OCP)
- L — Liskov Substitution principle (LSP)
- I — Interface Segregation principle (ISP)
- D — Dependency Inversion principle (DIP)
S — Single-Responsibility principle (SRP)
class หนึ่งควรมีเหตุผลเดียวที่ทำให้เกิดการเปลี่ยนแปลง หมายความว่า class ควรมีหน้าที่เพียงหนึ่งเดียว หลักการนี้ส่งเสริมการแยกหน้าที่ออกจากกัน ซึ่งจะทำให้ระบบง่ายต่อการจัดการโดยแบ่งฟังก์ชันการทำงานออกเป็น class ย่อย ๆ ที่มุ่งเน้นไปที่งานเดียว
Example (SRP)
/// BAD
public class Report
{
public void GenerateReport()
{
// Code to generate report
}
public void SendEmail()
{
// Code to send email
}
}/// GOOD
public class ReportGenerator
{
public void GenerateReport()
{
// Code to generate report
}
}public class EmailNotifier
{
public void SendEmail()
{
// Code to send email
}
}
พิจารณาคลาส Report
ที่จัดการการสร้างรายงานและการส่งอีเมล ตามหลักการ SRP หน้าที่เหล่านี้ควรถูกแยกออกเป็นคลาสต่างหาก ซึ่งเมื่อเราแยกมาเป็น ReportGenerator
และ EmailNotifier
การแยกนี้จะทำให้ระบบง่ายต่อการบำรุงรักษาและทดสอบ
O — Open/Closed principle (OCP)
องค์ประกอบของซอฟต์แวร์ (เช่น คลาส โมดูล ฟังก์ชัน ฯลฯ) ควรเปิดให้ขยายได้แต่ปิดการแก้ไข หมายความว่า behavior ของโมดูลสามารถขยายได้โดยไม่ต้องแก้ไข code ที่มีอยู่แล้ว โดยทั่วไปผ่านการสืบทอดหรือการนำอินเทอร์เฟซไปใช้
Example (OCP)
/// BAD
public class Shape
{
public string Type { get; set; }
public double Area()
{
if (Type == "Circle")
{
// Calculate area of circle
}
else if (Type == "Square")
{
// Calculate area of square
}
// More shapes
}
}/// GOOD
public abstract class Shape
{
public abstract double Area();
}public class Circle : Shape
{
public double Radius { get; set; }
public override double Area()
{
return Math.PI * Radius * Radius;
}
}public class Square : Shape
{
public double Side { get; set; }
public override double Area()
{
return Side * Side;
}
}
หากมีคลาส Shape
ที่มีเมธอด Area()
แทนที่จะแก้ไขคลาส Shape
เพื่อรองรับรูปร่างใหม่ คุณสามารถขยายมันได้โดยการสร้างซับคลาส เช่น Circle
และ Square
ที่แต่ละคลาสจะมีการนำเมธอด Area()
ไปใช้
L — Liskov Substitution principle (LSP)
Object ของ superclass ควรถูกแทนที่ได้ด้วย Object ของ subclass โดยไม่กระทบต่อความถูกต้องของโปรแกรม หลักการนี้ทำให้แน่ใจว่า subclass สามารถใช้แทนที่ superclass ได้ ส่งเสริมการใช้ ‘polymorphism’
Example (LSP)
/// BAD
public class Bird
{
public virtual void Fly()
{
// Flying logic
}
}
public class Penguin : Bird
{
public override void Fly()
{
throw new Exception("Penguins can't fly");
}
}/// GOOD
public abstract class Bird
{
public abstract void Move();
}public class FlyingBird : Bird
{
public override void Move()
{
Fly();
} private void Fly()
{
// Flying logic
}
}public class Penguin : Bird
{
public override void Move()
{
Swim();
} private void Swim()
{
// Swimming logic
}
}
หาก Bird
เป็นซุปเปอร์คลาสและ Penguin
เป็นซับคลาส เมธอดและคุณสมบัติทั้งหมดของ Bird
ควรทำงานได้กับอินสแตนซ์ของ Penguin
ถ้า Bird
มีเมธอด Fly()
แต่ Penguin
ไม่สามารถบินได้ จะเป็นการละเมิด LSP เพื่อปฏิบัติตาม LSP ให้แน่ใจว่า Penguin
ไม่ได้สืบทอดเมธอด Fly()
จาก Bird
I — Interface Segregation principle (ISP)
Class ทุก class ไม่ควรถูกบังคับให้ใช้ Interface ที่บังคับให้ implement method ที่พวกเขาไม่ได้ใช้ หลักการนี้ส่งเสริมการสร้าง Interface ที่เฉพาะเจาะจง
Example (ISP)
/// BAD
public interface IMachine
{
void Print();
void Scan();
void Fax();
}
public class MultiFunctionPrinter : IMachine
{
public void Print()
{
// Print logic
} public void Scan()
{
// Scan logic
} public void Fax()
{
// Fax logic
}
}/// GOOD
public interface IPrinter
{
void Print();
}public interface IScanner
{
void Scan();
}public interface IFax
{
void Fax();
}public class MultiFunctionPrinter : IPrinter, IScanner, IFax
{
public void Print()
{
// Print logic
} public void Scan()
{
// Scan logic
} public void Fax()
{
// Fax logic
}
}public class SimplePrinter : IPrinter
{
public void Print()
{
// Print logic
}
}
Interface IMachine
บังคับให้คลาสที่ใช้งานต้องมีวิธีการทั้งหมด แม้ว่าจะไม่จำเป็นต้องใช้งานทั้งหมด ดังนั้นเราต้องแยก Interface ออกเป็น IPrinter
, IScanner
, และ IFax
ทำให้คลาสที่นำไปใช้สามารถเลือกใช้เฉพาะวิธีการที่ต้องการ
D — Dependency Inversion principle (DIP)
โมดูลระดับสูงไม่ควรพึ่งพาโมดูลระดับต่ำ แต่ทั้งสองควรพึ่งพาอาศัยการแยกนามธรรม (abstraction) หลักการนี้มุ่งหวังการลดความเกี่ยวพันของโมดูลซอฟต์แวร์ ทำให้สามารถนำกลับมาใช้ใหม่และจัดการได้ง่ายขึ้น
/// BAD
public class WiredKeyboard
{
// Wired keyboard implementation
}
public class Computer
{
private WiredKeyboard _keyboard; public Computer()
{
_keyboard = new WiredKeyboard();
}
}/// GOOD
public interface IKeyboard
{
// Keyboard interface
}public class WiredKeyboard : IKeyboard
{
// Wired keyboard implementation
}public class WirelessKeyboard : IKeyboard
{
// Wireless keyboard implementation
}public class Computer
{
private IKeyboard _keyboard; public Computer(IKeyboard keyboard)
{
_keyboard = keyboard;
}
}// Usage:
IKeyboard wiredKeyboard = new WiredKeyboard();
Computer computer = new Computer(wiredKeyboard);
class Computer
พึ่งพา class WiredKeyboard
โดยตรง ซึ่งทำให้ระบบยืดหยุ่นน้อยลงดังนั้นเราต้องใช้การแยก abstraction ผ่านอินเทอร์เฟซ IKeyboard
ทำให้ class Computer
ไม่พึ่งพาการใช้งานเฉพาะของคีย์บอร์ด ทำให้ระบบยืดหยุ่นและขยายได้ง่ายขึ้น
ข้อดีของหลักการ SOLID
การปฏิบัติตามหลักการ SOLID มีข้อดีหลายประการ:
- การบำรุงรักษาที่ดีขึ้น: Code ง่ายต่อการเข้าใจและแก้ไข ลดความเสี่ยงในการสร้าง Bugs
- ความยืดหยุ่นที่เพิ่มขึ้น: ระบบสามารถปรับตัวให้เข้ากับการเปลี่ยนแปลงและข้อกำหนดใหม่ด้วยผลกระทบที่ต่ำต่อ code ที่มีอยู่
- การนำกลับมาใช้ใหม่ที่เพิ่มขึ้น: การออกแบบโมดูลส่งเสริมการใช้โค้ดซ้ำ ประหยัดเวลาและความพยายามในการพัฒนา
- ความสามารถในการทดสอบที่ดีขึ้น: class ที่เล็กและเน้นเฉพาะ และโมดูลที่แยกจากกันง่ายต่อการทดสอบอย่างเป็นอิสระ
สรุป
หลักการ SOLID เป็นพื้นฐานที่แข็งแกร่งสำหรับการออกแบบเชิงวัตถุ (OOP) ชี้แนะแนวทางให้นักพัฒนาสร้างซอฟต์แวร์ที่มีการปรับขยายง่าย บำรุงรักษาง่าย และยืดหยุ่น ด้วยการผนวกหลักการเหล่านี้เข้ากับการปฏิบัติในการพัฒนาซอฟต์แวร์ คุณจะสามารถเพิ่มคุณภาพและความยืนยาวของโครงการซอฟต์แวร์ของคุณได้อย่างมาก
สามารถอ่านเพิ่มเติมได้ที่
SOLID Design Principles in Software Development (freecodecamp.org)
SOLID: The First 5 Principles of Object Oriented Design | DigitalOcean
ขอบคุณที่เข้ามาอ่านกันครับผม :D