در دنیای پرشتاب توسعه نرمافزار، نوشتن کدِ عالی، قابل نگهداری و مقیاسپذیر بسیار مهم است. یک راه برای دستیابی به این هدف، پیروی از مجموعهای از اصول طراحی بنیادی به نام اصول SOLID است. این اصول یک چارچوب واضح برای ساخت نرمافزارهایی ارائه میدهند که درک، توسعه و نگهداری آنها آسان است. در این متن، ما اصول SOLID را بررسی خواهیم کرد و به طور دقیق به تکتک اجزای آن خواهیم پرداخت. ما راهنماهای عملی پیادهسازی و بهترین روشها برای اعمال آنها را مرور خواهیم کرد. حال بیایید کاوش خود را با یک مرور کلی مختصر از اصول SOLID آغاز کنیم.
اصول SOLID مجموعهای از پنج اصل طراحی اساسی هستند که توسط «رابرت سی مارتین» برای راهنمایی توسعهدهندگان نرمافزار در ایجاد سیستمهای نرمافزاری قابل نگهداری، مقیاسپذیر و انعطافپذیر معرفی شدهاند. این اصول، زمانی که رعایت شوند، به توسعه نرمافزاری کمک میکنند که درک، اصلاح و توسعه آن در طول زمان آسانتر باشد.
حروف اختصاری SOLID مخفف موارد زیر است:
اهمیت اصول طراحی در توسعه نرمافزار
اصول طراحی، مانند اصول،SOLID به دلیل متعددی نقش محوری در فرایند توسعه نرمافزار ایفا میکنند.
حرف "S" در اصول SOLID مخفف Single Responsibility Principle (SRP) است که بیان میکند یک کلاس باید فقط یک دلیل برای تغییر داشته باشد، یا بهعبارتدیگر، باید یک مسئولیت یا کار مشخص و خوبی تعریف شده در یک سیستم نرمافزاری داشته باشد.
بیایید نگاهی به یک نمونه کد جاوا در زیر بیندازیم که به طور واضح اصل SRP را نقض میکند:
در مثال بالا، کلاس کارمند (Employee) دو مسئولیت دارد: محاسبه حقوق کارمند و ایجاد گزارش حقوق و دستمزد. این کار اصل مسئولیت واحد (SRP) را نقض میکند؛ زیرا دلیل بیشتری برای تغییر دارد.
برای رفع نقض SRP در مثال قبلی، بیایید کد را برای تفکیک دغدغهها بازنگری کنیم و اطمینان حاصل کنیم که هر کلاس یک مسئولیت واحد و بهخوبی تعریف شده دارد. ما کلاسهای متمایزی برای محاسبه حقوق کارمند و ایجاد گزارش حقوق و دستمزد ایجاد خواهیم کرد.
در راهحل بازنگری شده، مسئولیتهای محاسبه حقوق و ایجاد گزارش حقوق و دستمزد تقسیم شده است که هر PayrollReportGenerator و Employee به دو کلاس مجزا کدام یک مسئولیت واحد دارند. این با اصل SRP مطابقت دارد. بیایید نگاهی به نمایش کلاسها و پیادهسازی SRP بیندازیم.
خطاها و اشتباههای رایج
استفاده از SRP با خطاها و اشتباهات رایجی همراه است که باید آنها را بشناسیم. درک این چالشها به ما امکان میدهد تا بهتر از آنها اجتناب کنیم و کد بهتر و قابل نگهداری بیشتری ایجاد کنیم. بیایید برخی از این مشکلات بالقوه را بررسی کنیم.
بهترین راهحلها:
برای رعایت مؤثر،SRP نکات زیر را در نظر بگیرید:
مسئولیتها یا نقشهایی را که هر کلاس باید داشته باشد به طور واضح تعریف کنید. از مخلوطکردن وظایف نامرتبط در یک کلاس واحد خودداری کنید. از اصل جداسازی نگرانیها (SoC(Separation of Concerns برای تقسیم عملکرد برنامهٔ خود به ماژولها یا کلاسهای مجزا استفاده کنید که هر کدام مسئول یک دغدغه خاص هستند.
اگر کلاسی با چندین مسئولیت پیدا کردید، آن را به کلاس های کوچکتر و متمرکزتر با یک مسئولیت واحد برای هر کدام، بازنگری کنید. کلاسهای خود را بهگونهای طراحی کنید که تغییرات در یک مسئولیت بر پیادهسازی سایر مسئولیتها تأثیری نداشته باشد. برای اطمینان از رعایت SRP توسط کلاسها، بررسی کد انجام دهید. اعضای تیم را تشویق کنید تا در مورد مسئولیتهای کلاسهای بازخورد ارائه دهند.
اصل باز/بسته (Open/Closed Principle)OCP بیان میکند که موجودیتهای نرمافزار (مانند کلاسها، ماژولها و توابع) باید برای توسعه باز باشند؛ اما برای تغییر بسته باشند. بهعبارتدیگر، شما باید بتوانید بدون تغییر کد منبع موجود، قابلیت یا رفتار جدیدی را به یک موجودیت نرمافزار اضافه کنید. برای درک بهتر اصل باز/بسته (OCP) بیایید نگاهی به یک نمونه کد جاوا بیندازیم که به طور واضح این اصل را نقض میکند.
در مثال بالا، کلاس مستطیل (Rectangle) برای محاسبه مساحت یک مستطیل طراحی شده است. بااینحال، اگر میخواهید آن را برای کار با اشکال دیگر مانند دایره یا مثلث گسترش دهید، باید کلاس را اصلاح کنید که این کار اصل باز/بسته (OCP) را نقض میکند.
رفع نقض اصل باز/بسته (OCP)
ما اصل باز/بسته (OCP) را اعمال خواهیم کرد و خواهیم دید که چگونه میتوانیم مشکل ذکر شده در بالا را حل کنیم.
در این راهحل بازنگری شده، کلاس Shape بهعنوان یک کلاس پایه انتزاعی برای همه اشکال معرفی شده است. هر شکل (Shape ) خاص (Rectangle, Circle) کلاس Shape را گسترش میدهد و پیادهسازی خاص خود از متد calculateArea را ارائه میدهد. این با اصل باز/بسته (OCP) مطابقت دارد؛ زیرا شما میتوانید بدون تغییر کد موجود، اشکال جدید اضافه کنید. در زیر نمایش بصری کلاس ها و پیادهسازی اصل باز/بسته (OCP) آمده است.
خطاها و اشتباهات رایج
بیایید برخی از خطاها و اشتباهات رایجی را که توسعهدهندگان ممکن است با آنها مواجه شوند بررسی کنیم:
بهترین راهحلها:
برای پیادهسازی مؤثر اصل باز/بسته (OCP) باید نکات زیر را در نظر بگیرید:
اصل Liskov Substitution Principle یا بهاختصار (LSP) بیان میکند که اشیا یک کلاس مشتق شده (subtype) باید بتوانند بدون تأثیر بر عملکرد برنامه، اشیای کلاس پایه (supertype) را جایگزین کنند. بهعبارتدیگر، اگر کلاس A زیرمجموعهای از کلاس B باشد، نمونههای کلاس B باید بدون ایجاد مشکل جایگزین نمونههای کلاس A شوند.
نمونهای از نقض اصل جانشینی Liskov
برای درک بهتر اصل جانشینی Liskov بیایید نگاهی به یک نمونه کد جاوا بیندازیم که به طور واضح این اصل را نقض میکند.
در این مثال، کلاس شترمرغ (Ostrich) زیرمجموعهای از کلاس پرنده (Bird) است. بااینحال، اصل جانشینی LSP را نقض میکند؛ زیرا متد fly را که ارتباطی با شترمرغ ندارد (زیرا شترمرغ نمیتواند پرواز کند) را override میکند. این بدان معنی است که نمیتوان یک نمونه از شترمرغ را بدون ایجاد رفتار غیرمنتظره جایگزین نمونهای از پرنده کرد.
رفع نقض LSP
ما اصل LSP را اعمال خواهیم کرد و خواهیم دید که چگونه میتوانیم مشکل ذکر شده در بالا را حل کنیم:
در این مثال اصلاح شده، کلاس شترمرغ زیرمجموعهای از کلاس پرنده (Bird) است و متد move را برای ارائه رفتاری معنادار که با اصل LSP مطابقت دارد را override میکند. یک نمونه از شترمرغ را میتوان بدون ایجاد مشکل جایگزین نمونهای از کلاس پرنده کرد. در اینجا نمایش بصری کلاسها و پیادهسازی اصل LSP آمده است.
خطاها و اشتباهات رایج
بهترین راهحلها
برای پیادهسازی مؤثر اصل جانشینی، Liskov نکات زیر را در نظر بگیرید:
حرف "I" در حروف اختصاری SOLID مخفف Interface Segregation Principle (ISP) است که بر این نکته تأکید دارد که سرویس کلاینتها کلاسها یا مؤلفههایی که از رابطها (interfaces) استفاده میکنند، نباید مجبور شوند به رابطهایی که استفاده نمیکنند وابسته باشند. بهعبارتدیگر، یک رابط باید مجموعه خاصی و متمرکزی از متدها داشته باشد که به کلاسهای پیادهسازی شده مرتبط هستند.
نمونهای از نقض اصل جداسازی رابط (ISP)
بیایید یک نمونه کد جاوا را بررسی کنیم که به طور واضح این اصل را نقض میکند:
در این مثال، اینترفیس Worker حاوی سه متد ()eat و () work و ()sleep است. بااینحال، ممکن است همه کلاسهایی که این اینترفیسهایی را پیادهسازی میکنند به همه این متدها نیاز نداشته باشند. این کار میتواند منجر به این شود که کلاسها مجبور به پیادهسازی متدهایی شوند که برای عملکرد آنها بیربط هستند و اصل جداسازی رابط (ISP) را نقض میکنند.
رفع نقض اصل جداسازی رابط (ISP)
ما اصل جداسازی رابط (ISP) را اعمال خواهیم کرد و خواهیم دید که چگونه میتوانیم مشکل ذکر شده در بالا را حل کنیم.
در این راهحل بازنگری شده، رابط Worker به سه اینترفیس کوچکتر و متمرکزتر تقسیم شده است: قابلاجرا (Workable) قابلخوردن (Eatable) و قابل خواب (Sleepable) هستند. حال، کلاسها میتوانند فقط اینترفیسهایی را که با عملکرد آنها مرتبط هستند را پیادهسازی کنند که مطابق با اصل جداسازی رابط (ISP) است.
نمایش بصری اینترفیسهای بازنگری شده (Sleepable ،Eatable ،Workable) مطابق با اصل جداسازی رابط (ISP) در زیر نشاندادهشده است.
خطاها و اشتباهات رایج
بیایید برخی از خطاها و اشتباهات رایجی را که توسعهدهندگان هنگام اعمال اصل جداسازی رابط (ISP) ممکن است با آنها مواجه شوند، بررسی کنیم:
ایجاد رابطهای بزرگ و یکپارچه با روشهای زیاد میتواند منجر به پیادهسازی متدهایی در کلاسها شود که به آنها نیاز ندارند و این امر اصل جداسازی رابط (ISP) را نقض میکند. تغییر یک اینترفیس برای اضافه یا حذف متدها میتواند پیادهسازیهای موجود را بشکند. هنگام ایجاد تغییرات، توجه به تأثیر آن بر سرویسگیرندهها Client مهم است.
بهترین راهحلها
برای رعایت مؤثر اصل جداسازی رابط (ISP) نکات زیر را در نظر بگیرید:
اصل وارونگی وابستگی Dependency Inversion Principle (DIP) بیان میکند که ماژولهای سطح بالا (یا کلاسها) نباید به ماژولهای سطح پایین وابسته باشند؛ بلکه هر دو باید به انتزاع رابطها یا کلاسهای انتزاعی (interfaces or abstract classes) وابسته باشند. به عبارت سادهتر، این اصل استفاده از رابطهای انتزاعی را برای جداکردن کامپوننتهای سطح بالا از جزئیات سطح پایین تشویق میکند.
نمونهای از نقض اصل وارونگی وابستگی (DIP)
برای درک بهتر اصل وارونگی وابستگی (DIP) بیایید نگاهی به یک نمونه کد جاوا بیندازیم که به طور واضح این اصل را نقض میکند:
در این مثال، کلاس Switch مستقیماً به کلاس LightBulb وابسته است که یک جزئیات سطح پایین است. این نقضی بر اصل وارونگی وابستگی (DIP) است؛ زیرا ماژولهای سطح بالا مانند Switch نباید به جزئیات سطح پایین وابسته باشند.
رفع نقض اصل وارونگی وابستگی (DIP)
ما اصل وارونگی وابستگی (DIP) را اعمال خواهیم کرد و خواهیم دید که چگونه میتوانیم مشکل ذکر شده در بالا را حل کنیم:
در این راهحل بازنگری شده، کلاس Switch به رابط Switchable وابسته است که یک انتزاعی است. کلاس LightBulb رابط Switchable را پیادهسازی میکند. این با اصل وارونگی وابستگی (DIP) مطابقت دارد؛ زیرا ماژولهای سطح بالا اکنون به انتزاعها (abstractions) بهجای جزئیات سطح پایین وابسته هستند.
نمایش بصری وارونگی وابستگی، نشان میدهد که چگونه ماژولهای سطح بالا (Switch) به انتزاعها (Switchable) بهجای پیادهسازیهای واقعی (LightBulb) وابسته هستند که در زیر نشاندادهشده است:
خطاها و اشتباهات رایج
همانطور که اصل وارونگی وابستگی (DIP) را یاد میگیریم، بیایید برخی از خطاها و اشتباهات رایجی را که توسعهدهندگان ممکن است با آنها مواجه شوند بررسی کنیم:
برای پیادهسازی مؤثر اصل وارونگی وابستگی (DIP) نکات زیر را در نظر بگیرید:
اصول SOLID راهنماهای اساسی برای طراحی و نگهداری سیستمهای نرمافزاری باکیفیت بالا، انعطافپذیر و قدرتمند هستند. تشویق به کاربرد این اصول در شیوههای توسعه نرمافزار میتواند منجر به توسعه کارآمدتر، باگ کمتر و تکامل نرمافزار بهصورت روانتر در طول زمان شود. پذیرش اصول SOLID سرمایهگذاری برای موفقیت بلندمدت و پایداری پروژههای نرمافزاری است.