
اصل SRP می گوید "هر ماژول نرم افزاری، کلاس یا تابع باید تنها یک دلیل برای تغییر داشته باشد". این بدان معناست که هر کلاس یا ساختار مشابه در کد شما باید فقط یک کار برای انجام دادن داشته باشد. همه چیز در آن کلاس باید به یک هدف واحد مرتبط باشد. این بدان معنا نیست که کلاس های شما فقط باید دارای یک متد یا ویژگی باشند. ممکن است اعضای زیادی وجود داشته باشند به شرطی که به مسئولیتی واحد مربوط باشند. اصل SRP راه خوبی برای شناسایی کلاسها در مرحله طراحی یک برنامه کاربردی به ما میدهد و باعث میشود به تمام راههایی فکر کنید که یک کلاس میتواند تغییر کند. تفکیک مناسب مسئولیت ها تنها زمانی انجام می شود که تصویر کاملی از نحوه عملکرد برنامه داشته باشیم.
نکته : اصل SRP یکی از مهمترین جنبه ها در نوشتن کد با قابلیت نگهداری خوب است.
اجازه بدهید این موضوع را با یک مثال بررسی کنیم:
ماژولی را در نظر بگیرید که گزارشی را جمع آوری و چاپ می کند. تصور کنید چنین ماژولی به دو دلیل قابل تغییر است.
این دو چیز به دلایل مختلف تغییر می کنند. اصل SRP می گوید که این دو جنبه از مشکل در واقع دو مسئولیت جداگانه هستند و بنابراین باید در کلاس ها یا ماژول های جداگانه باشند. طراحی بدی است که دو چیز را که به دلایل مختلف در زمانهای مختلف تغییر میکنند، با هم ترکیب کنیم. دلیل اینکه تمرکز یک کلاس روی یک دغدغه مهم است این است که کلاس را قوی تر می کند. در ادامه با مثال بالا، اگر تغییری در روند جمعآوری گزارش ایجاد شود، اگر کد چاپ بخشی از همان کلاس باشد، خطر شکسته شدن آن بیشتر است.
اگر یک کلاس چندین مسئولیت مختلف داشته باشد در نتیجه ممکن است دلایل مختلفی برای تغییر کردن داشته باشد. هم چنین این احتمال وجود دارد که تغییر در یکی از مسئولیتهایش روی رفتار کلاس در سایر مسئولیتهای نامرتبط تاثیر منفی بگذارد بدون آنکه شما اطلاع داشته باشید.
نکته : کلاس ها می توانند با هم کار کنند تا وظایف پیچیده بزرگتری را انجام دهند، اما هر کلاس باید یک تابع را از ابتدا تا انتها انجام دهد تا خروجی را به کلاس دیگر ارسال کند.
نکته : مزیت برنامه هایی که از SRP پیروی می کنند این است که می توانید رفتار یک تابع را با ویرایش کلاس واحدی که مسئول آن است تغییر دهید. همچنین، اگر یک عملکرد منفرد خراب شود، میدانید که اشکال در کد کجا خواهد بود و میتوانید اعتماد کنید که فقط آن کلاس خراب میشود. این فاکتور همچنین به خوانایی کد کمک می کند زیرا شما فقط باید یک کلاس را بخوانید تا عملکرد آن را مشخص کنید.
نکته : اصلSRP پیروی از یکی دیگر از اصول معتبر OOP، یعنی کپسوله سازی(encapsulation) را آسان می کند. پنهان کردن داده ها از کاربر زمانی که همه داده ها و روش های یک کار در یک کلاس تک مسئولیتی قرار دارند، آسان است.
پیاده سازی (Implementation)
بیایید به مثالی نگاه کنیم که چگونه SRP می تواند برای خوانایی بیشتر کلاس RegisterUser ما اعمال شود.
// does not follow SRP public class RegisterService { public void RegisterUser(string username) { if (username == "admin") throw new InvalidOperationException(); SqlConnection connection = new SqlConnection(); connection.Open(); SqlCommand command = new SqlCommand("INSERT INTO [...]"); SmtpClient client = new SmtpClient("smtp.myhost.com"); client.Send(new MailMessage()); //Send a welcome email. } }
برنامه فوق از SRP پیروی نمی کند زیرا RegisterUser سه کار مختلف انجام می دهد: ثبت نام کاربر، اتصال به پایگاه داده و ارسال ایمیل. این نوع کلاس باعث سردرگمی در پروژه های بزرگتر می شود، زیرا غیرمنتظره است که تولید ایمیل در همان کلاس ثبت نام وجود داشته باشد. همچنین موارد زیادی وجود دارد که میتواند باعث تغییر این کد شود، مثلاً اگر بخواهیم به جای sql از یک دیتابیس دیگر مثلا MongoDb استفاده کنیم یا اگر بجای ارسال ایمیل از sms برای ارسال پیام خوشامدگویی استفاده کنیم.
بیایید کد فوق را Refactor کنیم
public void RegisterUser(string username) { if (username == "admin") throw new InvalidOperationException(); _userRepository.Insert(...); _messageService.Send(...); }
اصل SRP رعایت شد زیرا RegisterUser فقط یک کاربر را ثبت می کند و تنها دلیلی که تغییر می کند این است که محدودیت های نام کاربری بیشتری اضافه شود. همه رفتارهای دیگر در برنامه حفظ می شوند.
مثلا فرض کنید ماژولی که وظیفه ارسال پیام را دارد تغییر می کند این تغییر رفتار هیچ تاثیری برروی RegisterUser ندارد.
بیشتر بخوانید : اصول طراحی نرم افزار Software Design Principles