آشنایی با قوانین پنج‌گانهٔ SOLID


SOLID دربرگیرندهٔ اصولی در برنامه‌نویسی شییٔ‌گرایی است که در اوایل سال 2000 توسط مهندسی به نام Robert Martin ابداع شد که تحت عنوان Uncle Bob یا «عمو باب» نیز شناخته می‌شود. وقتی این اصول به درستی در کنار یکدیگر به کار گرفته شوند، این امکان را به برنامه‌نویس یا توسعه‌دهنده می‌دهند تا با سهولت بیشتری دست به توسعهٔ نرم‌افزار بزند مضاف بر اینکه به‌کارگیریِ این اصول امکانی را به برنامه‌نویسان خواهد داد تا با رویکردی چابک به توسعهٔ نرم‌افزارهای خود پرداخته، از مرتکب شدن اشتباهات کوچک جلوگیری کنند و در صورت نیاز هم به سادگی اقدام به بازنویسی کدهای خود کنند (در فصل آینده، با مفهوم برنامه‌نویسی چابک بیشتر آشنا خواهیم شد.) به طور کلی، SOLID مخفف اصطلاحات زیر است:

Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle

Single Responsibility Principle
این قانون که به طور خلاصه SRP نیز نامیده می‌شود، حاکی از آن است که یک کلاس باید صرفاً یک وظیفه بیشتر نداشته باشد که در این صورت، کلاس‌ها فقط و فقط به خاطر ایجاد تغییر در وظیفه‌ای که انجام می‌دهند دستخوش تغییر خواهند شد نه چیز دیگر! کلاس‌ها می‌توانند فیچرهای مختلفی داشته باشند اما تمامی آن‌ها باید مربوط به یک حوزه بوده و مرتبط به هم باشند که در نهایت با محترم شمردن چنین قانونی، برنامه‌نویسان دیگر قادر نخواهند بود تا کلاس‌های اصطلاحاً همه‌فن‌حریف بنویسند.

Open-Closed Principle
هر کلاسی باید برای توسعه یافتن قابلیت‌هایش اصطلاحاً Open بوده و دست برنامه‌نویس برای افزودن فیچرهای جدید به آن باز باشد اما اگر وی خواست تا تغییری در کلاس ایجاد کند، چنین امکان باید Closed بوده و او اجازهٔ چنین کاری را نداشته باشد. فرض کنیم نرم‌افزاری نوشته‌ایم که دارای چندین کلاس مختلف است و نیازهای اپلیکیشن‌مان را مرتفع می‌سازند اما به جایی رسیده‌ایم که نیاز داریم قابلیت‌های جدید به برنامهٔ خود بیفزاییم. بر اساس این قانون، دست‌مان برای تغییر یا بهتر بگوییم افزودن فیچرهای جدید به کلاس مد نظر باز است در حالی که این قابلیت‌های جدید باید در قالب افزودن کدهای جدید صورت پذیرد نه ریفکتور کردن و تغییر کدهای قبلی!

برای روشن‌تر شدن این مسأله مثالی می‌زنیم. پیش از این با مفهوم وراثت در برنامه‌نویسی آشنا شدیم. فرض کنیم کلاسی داریم تحت عنوان BankAccount که دو کلاس دیگر تحت عناوین SavingAccount و InverstmentAccount از آن ارث‌بری می‌کنند و قصد داریم کلاس جدیدی تحت عنوان CurrentAccount ایجاد کنیم که از BankAccount ارث‌بری می‌کند اما این کلاس جدید دارای یکسری قابلیت‌هایی است که در کلاس والد دیده نشده‌اند که در چنین شرایطی به جای آنکه قابلیت‌های مد نظر جدید را به کلاس والد بیفزاییم، نیاز خود را از طریق افزودن قابلیت‌های جدید در همان کلاس فرزند عملی می‌کنیم. به عبارتی، هرگز دست به تغییر کدهای موجود نزده و قانون Open/Closed را هم به رسمیت شناخته‌ایم به طوری که کلاس مد نظرمان برای توسعه باز است اما برای اِعمال هر گونه تغییری بسته است.

Liskov Substitution Principle
این اصل حاکی از آن است که کلاس‌های فرزند باید آن‌قدر کامل و جامع از کلاس والد خود ارث‌بری کرده باشند که به سادگی بتوان همان رفتاری که با کلاس والد می‌کنیم را با کلاس‌های فرزند نیز داشته باشیم به طوری که اگر در شرایطی قرار گرفتید که با خود گفتید کلاس فرزند می‌تواند تمامی کارهای کلاس والدش را انجام دهد به جزء برخی موارد خاص، اینجا است که این اصل از SOLID را نقض کرده‌اید.

Interface Segregation Principle
پیش از این هم گفتیم که اینترفیس‌ها فقط مشخص می‌کنند که یک کلاس از چه متدهایی حتماً باید برخوردار باشد. در همین راستا و بر اساس این قانون، چندین اینترفیس تک‌منظوره به مراتب بهتر است از یک اینترفیس چندمنظوره است به طوری که اگر یک اینترفیس چندمنظورهٔ کامل و جامع داشته باشیم و سایر کلاس‌های ما از آن اصطلاحاً implements کنند،‌ در چنین صورتی ممکن است برخی خصوصیات، متدها و رفتارها را به برخی کلاس‌هایی که اصلاً نیازی به آن‌ها ندارند تحمیل کنیم اما اگر از چندین اینترفیس تخصصی استفاده کنیم، به سادگی می‌توانیم از هر اینترفیسی که نیاز داشته باشیم در کلاس‌های مد نظر خود استفاده نماییم و در صورتی هم کلاسی وجود داشت که نیاز به استفاده از چندین اینترفیس مختلف داشت، دست ما باز خواهد بود تا آن کلاس را از چندین اینترفیس implements کنیم.

Dependency Inversion Principle
درک این اصل از اصول پنج‌گانهٔ SOLID کمی دشوارتر به نظر می‌رسد و برخی تازه‌کارها آن را با Dependency Injection اشتباه می‌گیرند. به طور خلاصه، در OOP باید تمام تلاش خود را به کار بندیم تا Dependency (وابستگی) را مابین کلاس‌ها، ماژول‌ها و آبجکت‌های سطح بالا با ماژول‌های سطح پایین به حداقل برسانیم که با این کار، اِعمال تغییرات در آینده به مراتب راحت‌تر صورت خواهد پذیرفت.

در پایان اگر علاقمند به مطالعهٔ بیشتر در مورد این اصول هستید، توصیه می‌کنیم به دورهٔ آموزش قوانین SOLID مراجعه نمایید که در آن با ذکر مثال‌هایی واقعی، این قوانین توضیح داده شده‌اند.


لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان
کاربر میهمان
کاربر میهمانمن یک کاربر مهمان هستم
۱۳۹۸/۰۲/۲۹
در اصل 5 کلاس سطح بالا و پایین ینی چی؟
ممنون
سینا رستمی
سینا رستمی
۱۳۹۷/۰۳/۲۵
سلام ممنون از مطلب خوبتون
من منظور قسمت liskov substitution رو متوجه نمی شوم .مگر کلاسی (فرزند )که از کلاس دیگری(پدر) ارث بری میکند تمام متد ها و پراپرتی های آن را به ارث نمی برد ؟ پس ذکر liskov substitution که میگوید کلاس فرزند باید تمام ویژگی های والد رو داشته باشد و بتوان از آن به جای والد استفاده کرد به چه معنی است ؟
امیر
امیر
۱۳۹۵/۰۹/۲۹
...به سادگی می‌توانیم هر کلاسی که نیاز به هر کدام (+از) اینترفیس (+ها را) داشت (-را) از آن implements کنیم و در صورتی هم (+که) کلاسی وجود داشت...
کاربر میهمان
مریممن یک کاربر مهمان هستم
۱۳۹۵/۰۹/۲۱
فصل هفت یهو خیلی سخت شد:((((
کاربر میهمان
Majid Pour.من یک کاربر مهمان هستم
۱۳۹۵/۰۸/۱۴
برنامه نویس نیستم، اما اونقدر خوب توضیح دادین که به برنامه نویسی علاقمند شدم!
ممنون
کاربر میهمان
mahdikoochoolooمن یک کاربر مهمان هستم
۱۳۹۵/۰۴/۳۰
دمت گرم
امیر
امیر
۱۳۹۴/۱۱/۲۷
سلام implements کردن به معنای فراخوانی کردن است ؟
احمدرضا برفر
احمدرضا برفر
۱۳۹۴/۱۱/۱۳
واسه حرفه ای شدن باید خیلی ازین نکات رو مدنظر قرار داد ولی ره (شب) دراز است و قلندر بیدار
کاوه
کاوه
۱۳۹۴/۱۱/۱۲
یک سوال؟ اگر ما به رفتاری که در superclass بود احتیاج نداشتیم چی؟مثلا دوتا از کلاس های فرزند در همین مثال به سود نیاز داشتند ولی حساب جاری نیاز نداشته باشه.در این صورت ما سود رو جداگانه برای حساب های پس انداز و سرمایه گذاری تعریف کنیم؟یا نه...ممنون
مرتضی کارگر
مرتضی کارگر
۱۳۹۴/۰۹/۱۱
خوب بود...بازم ممنون