آشنایی با اصل Open/Closed در SOLID



visibility  
mode_comment   ۰

در برنامه نویسی بسیار مهم است که در زمان طراحی کد، قوانین استانداردسازی آن رعایت شود. استانداردسازی کدها باعث تولید کدهای بهینه، تمیز و خوانا می شود. شاید عده ای از برنامه نویسان (تعدادشان اصلا کم نیست!) تصور کنند صرف تحویل دادن یک برنامه که کار می کند (Working Software) کافی است. اما واقعیت این است که کدهای کثیف و غیر استاندارد معمولا با بهینه نبودن خود باعث می شوند که حتی کاربر برنامه از آن رضایت نداشته باشد. بنابراین در اغلب موارد کدهای کثیف و بی کیفیت حتی از دید کاربران عادی برنامه ها پنهان نمی ماند!

ضمنا تغییر کدهای کثیف و بی قانون بسیار مشکل، پر هزینه و زمان بر است. علاوه بر اینکه تغییر کدهای یک برنامه نویس کثیف برای برنامه نویسان همکار او دشوار است، برای خود او می تواند به یک کابوس واقعی تبدیل شود!

یکی از قوانین مهمی که در برنامه نویسی باید رعایت شود قوانین معروف به SOLID (بخوانید سالید) است. SOLID شامل پنج اصل است که همگی برای داشتن یک کد تمیز و استاندارد و حتی عقلانی (!) ضروری هستند. این پنج اصل عبارت اند از:

  1. Single Responsibility Principle یا اصل تک وظیفگی (SRP)
  2. Open/Closed Principle یا اصل باز و بسته بودن (OCP)
  3. Liskov Substitution Principle یا اصل جانشینی لیسکف (LSP)
  4. Interface Segregation Principle یا اصل تفکیک Interface (ISP)
  5. Dependency Inversion Principle یا اصل وارونه کردن وابستگی (DIP)

در این مطلب به آشنایی با اصل دوم یعنی اصل باز و بسته بودن می پردازیم.

اصل Open/Closed یا باز و بسته بودن چیست

اصل باز و بسته بودن یا اصل Open/Closed به نظر بسیاری، اساس برنامه نویسی شی گرا را تشکیل می دهد. رابرت مارتین (Robert C. Martin) که در بین برنامه نویسان به عمو باب (Uncle Bob) مشهور است با عبارت: "مهم ترین اصل طراحی شی گرا" از این اصل یاد کرده است.

موجودیت های برنامه که شامل کلاس ها، ماژول ها، توابع و... می  را در نظر بگیرید. بر اساس اصل باز و بسته بودن هر کدام از این موجودیت ها باید برای گسترده شدن (Extension) باز، اما برای تغییر کردن بسته باشند.

تصور کنید شخصی دست هایی شبیه قیچی داشته باشد! این دست ها باید برای کارهای روزمره هر بار به طور کامل عوض شوند (که البته عوض کردن دست در دنیای واقعی کمی دور از ذهن به نظر می رسد!). درست مانند کلاسی که تمام کارایی را داخل خود پیاده کرده است و توسعه دهنده برای تغییر کارایی و حتی گسترش آن، محتویات کلاس را دستکاری می کند. اما روش درست تر این است که دست ها به جای اینکه تبدیل به ابزار شوند، به ابزارهای مورد نیاز "مجهز شوند". در این حالت دست ها با گسترش کارایی یا Extend شدن تجهیز می شوند.

SOLID چیست

رعایت این اصل به شما کمک می کند مجبور نباشید برای تغییر یک کلاس، تمام کدهایی که از آن کلاس استفاده می کنند را تغییر دهید. به عنوان مثال به کد زیر توجه کنید:

در این نمونه کد، بر اساس خاصیت type از اشیای نوع shape، در یک ساختار switch، شی مناسب بر اساس آن با تابع drawSquare() و تابع drawCircle() کشیده می شود. در این ساختار switch دو نوع مربع (Square) و دایره (Circle) بررسی شده است. حال اگر بخواهیم شکل دیگری غیر از مربع و دایره را بکشیم چه باید کرد؟

همانطور که حدس می زنید مجبوریم یک case دیگر برای شکل جدید در switch وارد کنیم. برای هر شکل جدیدی مجبوریم switch را بارها و بارها تغییر دهیم و به کد اصلی دست ببریم که این دقیقا بر خلاف اصل باز و بسته بودن است! بنابراین مشکل را به شکل زیر حل می کنیم:

more  بیشتر بخوانید : اصل تفکیک Interface در SOLID

اینبار یک Interface با نام Shape تعریف کرده ایم. یک متد draw() درون این Interface تعریف شده است. هر کلاسی که بخواهد نوع جدیدی از شکل را معرفی کند مجبور است این Interface را Implement کرده و متد draw() را به سبک خود پیاده سازی کند.

سپس برای ساختن حلقه نمایش اشکال در متد drawAllShapes() مثل سابق لیست اشکال را از طریق پارامترهای متد دریافت می کنیم. در داخل حلقه کافی است متد draw() از هر شی shape موجود در لیست shapes را فراخوانی کنیم تا شکل به سبک خودش نمایش داده شود!

اصل باز و بسته بودن و Interface ها

در تعریف اول اصل باز و بسته بودن، برتراند میر (Bertrand Meyer) در کتاب با نام ساختار نرم افزار شی گرا (Object Oriented Software Construction) اینطور نوشته است:

info نکته :

"موجودیت های نرم افزار (کلاس، ماژول، تابع و...) باید برای گسترش باز اما برای تغییر بسته باشند."

اما مشکل این تعریف در آن است که به طور تنگاتنگی با Inheritance یا وراثت در رابطه است. بنابراین کلاس های فرزند با ارث بردن از کلاس والد تا حد زیادی به جزئیات ساختار والد وابسته می شدند. این روند باعث ایجاد اتصال تنگاتنگ (Tight Coupling) می شد.

برای آشنایی بیشتر با این مشکل بهتر است در مورد اصول طراحی کد بیشتر صحبت کنیم. به طور کلی اصول طراحی کد بر اساس این دو اصل استوار است:

  • High Cohesion یا انسجام بالا
  • Low Coupling یا اتصال کم (Loosely Coupling)

به طور خلاصه انسجام بالا یعنی این که متد های یک کلاس همه در خدمت یک هدف باشند. اتصال کم یعنی ماژول های یک برنامه نباید با یکدیگر در هم تنیده و بیش از حد متصل باشند. در صورت وجود چنین مشکلی در برنامه، اصطلاحا آن برنامه را (Tight Coupled) می نامند. در برنامه های در هم تنیده پس از ایجاد تغییر در یک کلاس مجبوریم کلاس های وابسته و مربوط دیگر را هم تغییر دهیم. یعنی تغییر کوچکی در برنامه باعث بر زمین ریختن آوار برنامه و ارور های مکرر می شود.

به اصل باز و بسته بودن بر می گردیم. گفتیم در صورتی که در این اصل از Inheritance استفاده کنیم، باعث ایجاد اتصال تنگاتنگ در برنامه می شویم. بنابراین عمو باب تعریف تازه ای از اصل باز و بسته بودن ارائه داد که به جای وراثت بر چندریختی (پلی مورفیسم) استوار است. حالا به جای سوپرکلاس ها یا کلاس های والد از Interface استفاده می کنیم. با این کار برای جایگزینی کد به جای درگیر شدن در جزئیات سوپرکلاس ها، تنها Interface ها را پیاده سازی می کنیم و به گسترش برنامه می پردازیم. اما به هر حال انتخاب بین وراثت و چند ریختی برای رعایت اصل باز و بسته بودن انتخاب شماست!

نتیجه گیری

با خواندن این مطلب مهم ترین اصل برنامه نویسی شی گرا را یاد گرفتیم! اصل باز و بسته بودن یکی از اصول پنجگانه از SOLID است. با رعایت کردن این اصل، با تغییر یک کلاس یا ماژول، احتیاجی به تغییر دادن همه کدهایی که از آن استفاده کرده اند نداریم. آیا شما تجربه ای از رعایت کردن یا نکردن این اصل داشته اید؟ استفاده از این اصل چه کمکی به شما برای داشتن کدهای بهتر کرده است؟ از خواندن نظرات شما خوشحال می شویم!

7Learn Experts
comment دیدگاه کاربران

add_circle ارسال دیدگاه

خوشحال میشیم دیدگاه و یا تجربیات خودتون رو با ما در میون بذارید :