Single Responsibility Principle (SRP) คือหลักการที่ว่า "A class should have only one reason to change" (คลาสควรมีเหตุผลเพียงอย่างเดียวที่จะต้องเปลี่ยนแปลง)
ความรับผิดชอบ (Responsibility) ในที่นี้หมายถึง "เหตุผลที่ทำให้ต้องแก้ไขคลาส" หากคลาสมีหลายเหตุผลที่ต้องเปลี่ยน แสดงว่ามีหลาย Responsibility ควรแยกออกเป็นคลาสต่างกัน
ตัวอย่าง: ก่อนใช้ SRP (Violation)
public class Program
{
public static void SendInvite(string email, string firstName, string lastName)
{
// 1. Validate Name
if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
throw new Exception("Name is not valid");
// 2. Validate Email
if (!email.Contains("@") || !email.Contains("."))
throw new Exception("Email is not Valid!");
// 3. Send Email (SMTP)
var client = new SmtpClient();
client.Send(new MailMessage("test@gmail.com", email)
{
Subject = "Please Join the Party!"
});
}
}
ปัญหา: คลาสนี้มี 3 เหตุผลที่จะเปลี่ยน: 1) เปลี่ยนกฎ Validate Name 2) เปลี่ยนกฎ Validate Email 3) เปลี่ยนวิธีส่งอีเมล (เช่น ใช้ API แทน SMTP)
ตัวอย่าง: หลังใช้ SRP (Compliant)
// 1. รับผิดชอบ Validate Name เท่านั้น
public static class UserNameService
{
public static void Validate(string firstName, string lastName)
{
if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
throw new Exception("Name is not valid");
}
}
// 2. รับผิดชอบ Validate Email เท่านั้น
public static class EmailService
{
public static void Validate(string email)
{
if (!email.Contains("@") || !email.Contains("."))
throw new Exception("Email is not Valid!");
}
}
// 3. รับผิดชอบส่งอีเมล (อาจแยก Interface ออกไปอีกระดับ)
public class EmailSender
{
public void Send(string from, string to, string subject)
{
var client = new SmtpClient();
client.Send(new MailMessage(from, to) { Subject = subject });
}
}
// 4. Orchestrator เรียกใช้ Services
public class InvitationService
{
private readonly EmailSender _emailSender;
public InvitationService(EmailSender emailSender)
{
_emailSender = emailSender;
}
public void SendInvite(string email, string firstName, string lastName)
{
UserNameService.Validate(firstName, lastName);
EmailService.Validate(email);
_emailSender.Send("test@gmail.com", email, "Please Join the Party!");
}
}
ประโยชน์
- แต่ละคลาสเล็ก เฉพาะเจาะจง ทดสอบง่าย (Unit Test)
- ลด Coupling ระหว่างส่วนต่างๆ
- เปลี่ยนแปลงได้อย่างอิสระ (เช่น เปลี่ยน EmailSender เป็น API ใหม่ ไม่กระทบ Validate)
- อ่านโค้ดเข้าใจง่าย
ข้อสังเกต
SRP ไม่ได้หมายถึง "คลาสทำได้แค่หนึ่งอย่าง" แต่มีความหมายว่า "มีเหตุผลเปลี่ยนแปลงเพียงอย่างเดียว" คลาสอาจทำหลายอย่างได้ ถ้าสิ่งเหล่านั้นเปลี่ยนแปลงไปพร้อมกันเสมอ (Cohesion สูง)