มาลองทำความรู้จัก SOLID — หลักการออกแบบในการพัฒนา Software

Krungsri Nimble
Krungsri Nimble
Published in
4 min readMay 28, 2024

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 มีข้อดีหลายประการ:

  1. การบำรุงรักษาที่ดีขึ้น: Code ง่ายต่อการเข้าใจและแก้ไข ลดความเสี่ยงในการสร้าง Bugs
  2. ความยืดหยุ่นที่เพิ่มขึ้น: ระบบสามารถปรับตัวให้เข้ากับการเปลี่ยนแปลงและข้อกำหนดใหม่ด้วยผลกระทบที่ต่ำต่อ code ที่มีอยู่
  3. การนำกลับมาใช้ใหม่ที่เพิ่มขึ้น: การออกแบบโมดูลส่งเสริมการใช้โค้ดซ้ำ ประหยัดเวลาและความพยายามในการพัฒนา
  4. ความสามารถในการทดสอบที่ดีขึ้น: class ที่เล็กและเน้นเฉพาะ และโมดูลที่แยกจากกันง่ายต่อการทดสอบอย่างเป็นอิสระ

สรุป

หลักการ SOLID เป็นพื้นฐานที่แข็งแกร่งสำหรับการออกแบบเชิงวัตถุ (OOP) ชี้แนะแนวทางให้นักพัฒนาสร้างซอฟต์แวร์ที่มีการปรับขยายง่าย บำรุงรักษาง่าย และยืดหยุ่น ด้วยการผนวกหลักการเหล่านี้เข้ากับการปฏิบัติในการพัฒนาซอฟต์แวร์ คุณจะสามารถเพิ่มคุณภาพและความยืนยาวของโครงการซอฟต์แวร์ของคุณได้อย่างมาก

สามารถอ่านเพิ่มเติมได้ที่

SOLID Design Principles in Software Development (freecodecamp.org)

SOLID: The First 5 Principles of Object Oriented Design | DigitalOcean

ขอบคุณที่เข้ามาอ่านกันครับผม :D

Written by Kittikawin L.

--

--

Krungsri Nimble
Krungsri Nimble

"กรุงศรี นิมเบิล" บริษัทในเครือธนาคารกรุงศรีฯ เป็นฮับในการสร้าง ดูแล และพัฒนาสร้างสรรค์นวัตกรรมทางด้านการเงินดิจิทัลที่ช่วยให้ชีวิตง่ายขึ้น