حتما در طول یادگیری برنامه نویسی، عبارت کد تمیز را بارها و بارها شنیده اید. کد تمیز ویژگی اسرار آمیزی است که باعث میشود کدهای شما خوانا و تغییر پذیر شوند. با کد تمیز شما تبدیل به شوالیه ای میشوید که همیشه آماده جنگ با اژدها است! مارتین فاولر (Martin Fowler)، شوالیه مشهور علم کد تمیز در اینباره میگوید:
هر احمقی میتواند کدی بنویسد که یک کامپیوتر آن را بفهمد. برنامه نویسهای خوب کدهایی مینویسند که انسانها میتوانند آن را بفهمند.
اما آیا تا به حال فکر کرده اید چه چیزی باعث میشود یک کد تمیز باشد یا نباشد؟ پیدا کردن کد کثیف با دنبال کردن بوی بد کد (Code Smell) ممکن میشود. در برنامه نویسی به علائمی که نشان میدهند یک کد تمیز نیست اصطلاحا بوی بد میگوییم. این علائم عبارت اند از:
سفتی یا Rigidity: ایجاد تغییر در کدها مشکل است
شکنندگی یا Fragility: طراحی کد برنامه به راحتی در هم میشکند و به هم میریزد
بی تحرکی یا Immobility: به سختی میتوان کدها را دوباره استفاده کرد. اصطلاحا Usability کد پایین است
چسبندگی یا Viscosity: انجام کار درست در کدها مشکل است!
به محض استشمام این بوهای بد در کدهای خودتان، باید به فکر یادگیری طراحی کد تمیز بیفتید. برای طراحی کد تمیز باید از یک سری اصول یا فوت کوزه گری پیروی کنید! یکی از این اصول SOLID است. SOLID بر پنج اصل استوار است:
Single Responsibility Principle یا اصل تک وظیفگی (SRP)
Open/Closed Principle یا اصل باز و بسته بودن (OCP)
Liskov Substitution Principle یا اصل جانشینی لیسکف (LSP)
Interface Segregation Principle یا اصل تفکیک Interface (ISP)
Dependency Inversion Principle یا اصل وارونه کردن وابستگی (DIP)
در این مطلب به معرفی اصل آخر SOLID یعنی اصل وارونگی وابستگی میپردازیم.
اصل وارونگی وابستگی چیست
اصل وارونگی وابستگی یا Dependency Inversion Principle تاکید میکند که ماژولهای سطح بالای برنامه نباید به ماژولهای سطح پایین آن وابسته باشند. رابرت مارتین (Robert Martin) مشهور به عمو باب در این رابطه میگوید:
الف: ماژولهای سطح بالا نباید به ماژولهای سطح پایین وابسته باشند. هر دوی آنها (ماژولهای سطح بالا و پایین) باید به Abstractionها وابسته باشند.
ب: Abstractionها نباید به جزئیات وابسته باشند. جزئیات باید به Abstractionها وابسته باشند.
خوب باید بگوییم که توضیح این اصل کمی مشکل است و گویا عمو هم در این رابطه خیلی موفق عمل نکرده! به زبان خیلی خیلی (و باز هم خیلی) ساده، ما باید همیشه از Interfaceها و Abstractها در برنامه نویسی استفاده کنیم.
یک پریز برق را در نظر بگیرید. تمام جزئیات سیم کشی برق در پشت پریز تعریف شده است. مثل اتصالات، سیمهای در هم پیچیده و.... تمام اینها ماژولهای سطح پایین سیستم برق هستند. سوکتهای برق دستگاههای مختلف الکترونیکی را هم ماژولهای سطح بالا در نظر میگیریم. با این تعریف در صورت نبود پریز، برای دریافت برق از سیم کشی ساختمان مجبور بودیم خودمان به طریقی سیمهای دستگاهها را به سیمهای سیم کشی برق متصل نگه داریم! بنابراین ماژولهای سطح بالا (سیم دستگاههای الکترونیکی) به ماژولهای سطح پایین (سیم کشی ساختمان) وابسته بودند.
اما به لطف پریز که یک Interface محسوب میشود، این وابستگی وارونه شده است. یعنی سیم کشی ساختمان طبق اصول این Interface قادر به اتصال برق به دستگاهها میشود. تمام دستگاهها نیز برای اتصال به سوراخهای این پریز باید قوانین آن را رعایت کنند. یعنی باید سوکت یا تبدیل مناسب با نوع پریز داشته باشند.
مثالی از اصل وارونه کردن وابستگی
در دنیای واقعی برنامه نویسی این اصل یکی از پرکاربردترین اصول در برنامه نویسی شی گراست. هر زمان که برای قطع وابستگی کدهای برنامه به یکدیگر از Interfaceها و Abstractها استفاده میکنید در حال پیاده سازی این اصل هستید.
احتمالا شما هم مانند بسیاری از برنامه نویسان در شروع کار، به روش سنتی به پایگاه داده متصل میشدید و اطلاعات را از آن دریافت و به آن ارسال میکردید. یعنی در بین کدهای در هم و برهم برنامه یک کد اتصال به دیتابیس مینوشتید و در جای جای برنامه به تناسب نیاز از آن استفاده میکردید. کوئریهای مختلف را میشد در هر جای کد شما به صورت پراکنده دید.
با در نظر گرفتن درایور پایگاه داده ای که استفاده میکردید (مثل MySQL، SQLServer، Oracle و...) به عنوان ماژول سطح پایین، ماژولهای سطح بالای شما یعنی کدهای برنامه به شدت به ماژولهای سطح پایین وابسته بود. فقط در ذهنتان تصور کنید چه میشد اگر میخواستید از MySQL به یک پایگاه داده NOSQL مثل MongoDB مهاجرت کنید؟ فاجعه! شما باید تمام کوئریهای وابسته را از جای جای برنامه پیدا میکردید و آنها را یکی یکی تغییر میدادید.
روش درست این است که یک Interface برای پایگاه داده در نظر بگیریم. تمام درایورهای مختلف برای استفاده شدن موظف میشوند این Interface را پیاده سازی یا Implement کنند. از آن پس کدهای برنامه فقط به آن رابطها متصل میشوند. به این ترتیب این وابستگی را وارونه کرده ایم!
با اهمیت کدهای تمیز و یاد گرفتن روش رسیدن به آن آشنا شدید. به نشانههای یک کد کثیف بوی بد کد میگوییم. این بوهای بد عبارت اند از: سفتی، شکنندگی، بی تحرکی و چسبندگی که هرکدام معنی خاص خود را دارند. یکی از موارد لازم برای رسیدن به کد تمیز، رعایت اصول SOLID در کدنویسی است. در این مطلب با اصل آخر از اصول SOLID یعنی اصل وارونه کردن وابستگی یا DIP آشنا شدید. به غیر از مثالی که گفتیم، شما چه نمونه هایی از استفاده اصل آخر در کدنویسی به خاطر دارید؟ از خواندن نظرات شما همیشه خوشحال میشویم!
۱۰ دیدگاه
محمد۲۲ آذر ۱۴۰۲، ۰۱:۴۶
در توضیح یک پیچیدگی شما باید مستقیم و شفاف فقط راجع به اون پیچیدگی صحبت کنید ..مثلا اینجا داریم راجع به DIP صحبت میکنیم دیگه آوردن اسم ماژول سطح بالا و پایین و اون مثال پریز که خواننده رو بیشتر گیج میکنه به نظرم درست نیست..حالا فرض کنید برای انواع دوشاخهها بخواییم پریز طراحی کنیم ...یعنی بالعکس..باید اشاره میشد که ماژول سطح بالا یعنی دستگاه برقی وماژول سطح پایین یعنی سیم کشی ساختمان یا بالعکس ...
حالا داستان پریز رو بیخیال یه مثال از ماژول سطح بالا و پایین در دنیای واقعی کد میزدیم تمامش بهتر حل میشد به نظرم
نازنین کریمی مقدم۰۳ دی ۱۴۰۲، ۱۰:۵۰
درود
فیدبک تون رو به تیم انتقال میدم انشاالله در مقالات بعدی این موضوع بیشتر رعایت بشه.
ممنون که با ما همراه هستید.
۰۸ فروردین ۱۴۰۱، ۰۹:۲۱
الف: ماژولهای سطح بالا نباید به ماژولهای سطح پایین وابسته باشند. هر دوی آنها (ماژولهای سطح بالا و پایین) نباید به Abstractionها وابسته باشند.
در جمله بالا نباید رو تغییر بدید به باید
high level modules should not depend on low level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.
نازنین کریمی مقدم۰۹ فروردین ۱۴۰۱، ۱۴:۴۰
درود
ممنون از اطلاع رسانی تون اصلاح شد :)
ماهان کبیر۱۱ آذر ۱۴۰۰، ۰۶:۴۷
خیلی عالی بود .. ممنون
Nazanin KarimiMoghaddam۱۳ آذر ۱۴۰۰، ۰۶:۱۰
ممنون که با ما همراه هستید
محسن۲۹ شهریور ۱۴۰۰، ۱۸:۱۳
عالی بود. ممنون
نازنین کریمی مقدم۳۱ شهریور ۱۴۰۰، ۱۰:۱۹
ممنون که با ما همراه هستید.
Fatemeh Chamanmah۲۵ فروردین ۱۳۹۹، ۱۲:۳۲
سلام. وقت بخیر. خدا قوت. ممنون بابت انتشار مقاله ی خوبتون.
یک اشتباه توی این قسمت وجود داشت:
الف: ماژولهای سطح بالا نباید به ماژولهای سطح پایین وابسته باشند. هر دوی آنها (ماژولهای سطح بالا و پایین) نباید به Abstractionها وابسته باشند.
درستش این میشه:
هر دوی آنها (ماژولهای سطح بالا و پایین) باید به Abstractionها وابسته باشند.
ممنونم
فاطمه چمن ماه۲۵ فروردین ۱۳۹۹، ۱۲:۲۸
سلام. وقت بخیر. ممنونم بابت انتشار این مقاله.
در قسمتی از متن اشتباهی وجود داره. ان قسمت که میگه :
الف: ماژولهای سطح بالا نباید به ماژولهای سطح پایین وابسته باشند. هر دوی آنها (ماژولهای سطح بالا و پایین) نباید به Abstractionها وابسته باشند.
درستش این هست:
هر دوی آنها (ماژولهای سطح بالا و پایین) باید به Abstractionها وابسته باشند.