اصول SOLID به زبان ساده - اصل اول
اصول SOLID به زبان ساده - اصل دوم
اصول SOLID به زبان ساده - اصل سوم
اصول SOLID به زبان ساده - اصل چهارم
اصل پنجم و آخر SOLID، اصل وارونگی وابستگی (Dependency Inversion Principle) نام داره که به اختصار DIP گفته میشه. توضیح رسمی و آکادمیک این اصل به صورت زیر هست. این توضیح رو بخونید تا با هم ریز به ریز جزییاتش رو بررسی کنیم:
کلاسهای سطح بالا نباید به کلاسهای سطح پایین وابسته باشن؛ هر دو باید وابسته به انتزاع (Abstractions) باشن. موارد انتزاعی نباید وابسته به جزییات باشن. جزییات باید وابسته به انتزاع باشن
خب دوستان این توضیحی بود که خیلی آکادمیک و یکم گنگ هست. مواردی مثل کلاس سطح بالا و سطح پایین، انتزاع و جزییات مواردی هستن که باید روشن بشن تا بتونیم این اصل رو خوب درک کنیم.
به کلاسهایی گفته میشه که مسئول عملیات اساسی و پایهای توی نرمافزار هستن. مثل کلاسی که با دیتابیس یا هارددیسک ارتباط برقرار میکنه، کلاسی که برای ارسال ایمیل استفاده میشه و ...
کلاسهایی که عملیات پیچیدهتر و خاصتری انجام میدن و برای انجام این کار از کلاسهای سطح پایین استفاده میکنن. برای مثال کلاس گزارشگیری برای ثبت و خوندن گزارش، به کلاس دیتابیس یا هارددیسک نیاز داره. کلاس Users، برای اطلاعرسانی به کاربرها به کلاس ایمیل نیاز داره.
کلاسهای انتزاعی کلاسهای هستن که قابل پیادهسازی نیستن اما به عنوان یک طرح و الگو برای کلاسهای دیگه در نظر گرفته میشن. مثلا یک کلاس انتزاعی برای گربه، زرافه، پلنگ و پنگوئن، میشه کلاس Animal. خود Animal به خودی خود قابل پیادهسازی نیست. بلکه یک طرح کلی برای حیوونایی هستن که مثال زدیم. پس تک تک این حیوونها یک ورژن کلیتر دارن که میتونیم اون رو Animal بنامیم. پیشنهاد میکنم این مقالهی مفصل درباره مفهوم انتزاع رو بخونید:
درک مفهوم انتزاع (Abstraction) در شیگرایی به زبان ساده
مفهوم انتزاع یا Abstraction توی برنامهنویسی شیگرا چیزی هست که باید درک بشه و ما توی این مقاله بطور مفصل با اون آشنا میشیم
منظور از جزییات توی تعریف این اصل، جزییات یک کلاس مثل نام و ویژگی پراپرتیها و متدهاست.
خب بپردازیم به بررسی این اصل. ابتدا کد زیر رو در نظر بگیرید:
class MySql { public insert() {} public update() {} public delete() {} } class Log { private database; constructor() { this.database = new MySql; } }
فرض کنیم یک کلاس سطح پایین داریم مثلا دیتابیس MySql
. و یک سری کلاس سطح بالا مثلاً گزارشگیری (Log
) از این کلاس استفاده میکنه. اگه بخوایم یک تغییر توی کلاس دیتابیس انجام بدیم، ممکنه بطور مستقیم تاثیر بذاره روی کلاسهایی که ازش استفاده میکنن. مثلا اگه توی کلاس MySql
اسم متد رو تغییر بدیم و یا پارامترها رو کم و زیاد کنیم، نهایتا توی کلاس Log
این تغییرات رو باید اعمال کنیم.
همچنین کلاسهای سطح بالا قابل استفاده مجدد نیستن. مثلاً اگه بخوایم برای کلاس Log
از دیتابیسهای دیگه مثلا MongoDB یا هارددیسک استفاده کنیم باید کلاس Log
رو تغییر بدیم یا یک کلاس جدا براساس هر نوع دیتابیس بسازیم.
خب همونطور که میبینید اگه یک کلاس سطح بالا وابسته به یک کلاس سطح پایین باشه این مشکلات به وجود میاد.
برای حل این مشکل باید با اینترفیس، یک لایه انتزاعی درست کنیم. با این کار کلاس Log
دیگه وابسته به یک کلاس خاص برای ذخیرهسازی و خوندن اطلاعات نیست و میتونیم هر نوع دیتابیسی رو استفاده کنیم و برای کلاس Log
اهمیتی نداره که با چه نوع دیتابیسی داره کار میکنه. چون وابسته به انتزاع هست.
ابتدا یک اینترفیس میسازیم برای اینکه کلاسهای سطح بالا و سطح پایین رو وابسته به این اینترفیس کنیم:
interface Database { insert(); update(); delete(); }
حالا کلاسهای سطح پایین باید این اینترفیس رو پیادهسازی کنن تا وابسته به انتزاع بشن:
class MySql implements Database { public insert() {} public update() {} public delete() {} } class FileSystem implements Database { public insert() {} public update() {} public delete() {} } class MongoDB implements Database { public insert() {} public update() {} public delete() {} }
و نهایتاً توی کلاسهای سطح بالا، وابستگی به یک کلاس خاص رو به اینترفیس واگذار میکنیم. کلاسهای سطح بالا زمانی وابسته به انتزاع میشن که بجای استفاده مستقیم از کلاسهای سطح پایین، از یک اینترفیس (رابط) استفاده کنن:
class Log { private db: Database; public setDatabase(db: Database) { this.db = db; } public update() { this.db.update(); } }
همونطور که میبینیم وابستگی به یک کلاس خاص از بین رفت و میتونیم هر نوع دیتابیسی رو برای کلاس Log استفاده کنیم:
logger = new Log; logger.setDatabase(new MongoDB); // ... logger.setDatabase(new FileSystem); // ... logger.setDatabase(new MySql); logger.update();
مثل بقیه اصول SOLID، این اصل هم تلاش داره وابستگی بین اجزا رو کمتر کنه تا بتونیم کدهای قابل نگهداری، تمیزتر و قابل توسعهتر بنویسیم. اما در نظر داشته باشید که مثل بقیه اصول توی دنیای برنامهنویسی، این اصل هم باید با چشم باز اعمال بشه. گاهی وقتا اعمال کردن یک سری اصول نه تنها مشکل رو حل نمیکنه، بلکه باعث پیچیدهتر شدن و گنگ شدن کد برنامه میشه.