یکی از خصیصههای معماری نرمافزاری خوب این است که «چیزهایی که ماهیت مشابهی داشته، به دلایل یکسانی دستخوش تغییر میشوند و در یک کلام، به یک خانواده تعلق دارند را باید در کنار یکدیگر قرار داد و الباقی را مجزا ساخت.»
از دید فنی، به چنین قابلیتی Single Responsibility Principle (اصل تک وظیفهای) یا به طور خلاصه SRP گفته میشود. به عبارت دیگر، این اصل حاکی از آن است که یک ماژول، کلاس، فانکشن یا هر چیزی میبایست وظیفهای واحد داشته، متمرکز بر یک تسک بوده و صرفاً به یک دلیل تغییر یابند نه اینکه به محض مواجه با یک نیاز در هر بخشی از نرمافزار، نیاز داشته باشیم تا آن را دستخوش تغییر سازیم. برای روشنتر شدن این مسأله، کلاسی فرضی تحت عنوان Employee که دارای ۳ فانکش مختلف است را در نظر میگیریم:
public class Employee
{
public function calculatePayment();
public function reportHours();
public function save();
}
برخی برنامهنویسان بر این باورند از آنجا که این ۳ فانکشن به نوعی مرتبط با یکدیگر هستند، میبایست در قالب یک کلاس تعریف شوند اما این در حالی است که طبق قانون SRP، این ۳ فانکشن بنا به دلایلی کاملاً متفاوت ممکن است نیاز به تغییر کردن داشته باشند. برای مثال، فانکشن ()calculatePayment زمانی میبایست تغییر یابد که حقوق و مزیا کم و زیاد شوند، فانکشن ()reportHours هم زمانی تغییر خواهد یافت که نحوهٔ گزارشدهی به مدیران تغییر کند و فانکشن ()save هم هر موقع که دیتابیس یا اسکمای برخی جداول تغییر یابد باید ریفکتور شود.
میبینیم که ۳ دلیل مختلف برای تغییر یافتن فانکشنها پیش روی ما است و این باعث میگردد که کلاس Employee بنا به هر دلیلی و به خاطر تغییر در سیاستهای مرتبط با هر یک از ۳ فانکشن زیرشاخهاش دستخوش تغییر قرار گیرد و نکتهٔ مهمتر اینکه اگر در دیگر بخشهای کد وابستگی به این کلاس وجود داشته باشد و کلاسهای دیگری از این کلاس ارثبری کرده باشند، آنها هم دستخوش تغییر خواهند شد!
یک معماری نرمافزاری خوب آن است که ما سورسکد را به بخشهای مجزایی تقسیمبندی کنیم که بتوان هر یک از آنها را به طور مجزا و بدون وابستگی به سایر بخشها مورد استفاده قرار داد. به عبارت دیگر، اگر ما بخشی را تغییر دادیم، دیگر بخشها نیاز به هیچگونه تغییری نداشته باشند.
اگر بخواهیم مجدد به مثال فوق بازگردیم، چنانچه کلاس Employee توسط دیگر کلاسهای ماژولهای مختلف پروژه استفاده شده باشد، بنابراین اعمال هرگونه تغییر در این کلاس، کلاسهای دیگر را تحتالشعاع خود قرار خواهد داد که در بسیاری از مواقع منجر به ایجاد باگ میشود. حال اگر بخواهیم کلاس فوق را بر اساس قانون SRP ریفکتور کنیم، خواهیم داشت:
public class Employee
{
public function calculatePayment();
}
public class EmployeeReporter
{
public function reportHours();
}
public class EmployeeRepository
{
public function save();
}
میبینیم که در کد فوق دارای ۳ کلاس مجزا از یکدیگر هستیم که هر کدام از آنها متمرکز بر تسکی اختصاصی هستند. به عبارت دیگر، تمامی فانکشنهای مرتبط با گزارشدهی را میتوان در کلاس EmployeeReporter قرار داد، تمامی فانکشنهای مرتبط با دیتابیس را در کلاس EmployeeRepository نوشت و هر آنچه که مرتبط با Business Rules (قوانین کاری) است را در کلاس Employee.
پیادهسازی اصولی SRP میتواند ضامن معماریهای نرمافزاری خوبی باشد که در آنها میزان Dependency (وابستگی) به صورت حداقلی بوده و اعمال یک تغییر در کد، منجر به ایجاد باگ در سایر بخشها نخواهد شد.