یکی از مشکلاتی که طراحی نامناسب برنامه های شی گرا برای برنامه نویسان ایجاد می کند موضوع مدیریت وابستگی در اجزای برنامه می باشد. اگر این وابستگی به درستی مدیریت نشود مشکلاتی شبیه موارد زیر در برنامه ایجاد می شوند:
برنامه ی نوشته شده را نمی توان تغییر داد و یا قابلیت جدید اضافه کرد. دلیل آن هم این است که با ایجاد تغییر در قسمتی از برنامه، این تغییر به صورت آبشاری در بقیه ی قسمت ها منتشر می شود و مجبور خواهیم بود که قسمت های زیادی از برنامه را تغییر دهیم. یعنی برنامه به یک برنامه ی ثابت و غیر پیشرفت تبدیل می شود. (این مشکل را Rigidity می نامیم.)
تغییر دادن برنامه مشکل است و آن هم به این دلیل که با ایجاد تغییر در یک قسمت از برنامه، قسمت های دیگر برنامه از کار می افتند و دچار مشکل می شوند. (این مشکل را Fragility می نامیم.
قابلیت استفاده مجدد از اجزای برنامه وجود ندارد. در واقع، قسمت های مجدد برنامه ی شی گرای شما آنچنان به هم وابستگی تو در تو دارند که به هیچ وجه نمی توانید یک قسمت را جدا کرده و در برنامه ی دیگری استفاده کنید.
هدف از SOLID توسعه کدهای با خوانایی بالاتر و توسعه پذیر تر و همچنین ایجاد یک استاندارد بین تمامی توسعه دهندگان است.
چرا SOLID
اصول 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 موجب بهبود فرآیند توسعه و تمیز بودن کد میشود.
استفاده از این اصول باعث میشود که یک استاندار مشترک در کل پروژه بوجود بیاید و تا حدودی مشکلات کار کردن روی پروژه های که بیش از یک توسعه دهنده در حال توسعه آن هستند را کاهش دهد. ولی این به این معنی نیست که که اگر تنها توسعه دهندهی پروژه هستید این اصول را رعایت نکنید.
در کل استفاده از اصول SOLID باعث نوشتن کد های با کارایی بالا (higher performance) ، امکان استفاده در جای مختلف (reusable) ، توسعه ساده تر (maintainable) ، مقیاس پذیر (scalable) و قابل تست (testable) میشود.