یک توسعه دهنده نرم افزار باید همیشه یک چیز را در ذهنش داشته باشد، در غیر اینصورت باید هزینه زیادی را متحمل شود یا حتی بدتر، باید شاهد مرگ محصولش باشد. تغییر.
مهم نیست که شما کجا کار می کنید، روی چه محصولی کار می کنید و با کدام زبان برنامه نویسی، برنامه نویسی می کنید. یک چیز، همیشه می تواند رخ بدهد. تغییر.
تنها یک چیز تغییر نمی کند. تغییر.
تغییر در کل چرخه حیات نرم افزار با ما خواهد بود. حتی می توان گفت که تغییر است که باعث تولد و مرگ یک نرم افزار می شود. پس نرم افزاری که ما باید طراحی و پیاده سازی کنیم، باید در مقابل تغییرات، دارای انعطاف زیادی باشد. و برای اینکار ما باید از همان ابتدای چرخه حیات مراقب و آگاه باشم تا طراحی خود را به شیوه ای انجام دهم تا در مقابل تغییرات کمترین هزینه سربار را متحمل شویم. یکی از اصول که می تواند ما را در این کار کمک کند و به بررسی آن خواهیم پرداخت، اصل open-closed می باشد. تقریبا کل این اصل بر تغییر تکیه دارد، اما تغییری که، نیاز به دستکاری کد موجود ندارد. اما چگونه در پایین به بررسی این موضوع خواهیم پرداخت.
اصل open-closed |
موجودیت های نرم افزاری برای نمونه کلاس ها، توابع و پیمانه ها باید برای توسعه باز باشند(اجازه داشته باشند)، اما برای تغییر باید بسته باشند(اجازه تغییر را ندارند). |
یک تشبیه زیبا(فکر می کنم به زبان اصلی خیلی زیباتر است.) |
Code should be closed(to change) like the lotus in the evening. Yet open(to extension) like the lotus flower in the morning. |
ما گفتیم که سیستم ها تغییر می کنند، ولی می خواهیم که کدهای نوشته شده خود را در برابر این تغییرات محافظت کنیم، و همچنین سیستم نرم افزاری خود را کسترش دهیم، شاید در ابتدا دو چیر کاملا مغایر به نظر رسید. اما ما به هر دو مورد نیاز داریم. تصور کنید که مشغول حل یک مسئله هستید، آیا می توانید تمام موارد جزئی و کلی آن را به طور کامل در نظر بگیرید، نیازمندیهای متفاوت مشتری ها و تغییرات نیازمندیها را به طور کامل پیش بینی کنید؟ مسلما جواب نه است. پس ما باید با دانش فعلی سیستم خود را طراحی کنیم، اما طراحی که قابلیت توسعه داشته باشید با کمترین هزینه دستکاری سیستم موجود.
فرض کنید که شما یک نرم افزاری نقاشی ساده می خواهید طراحی کنید که قابلیت رسم شکلهای مختلف را داشته باشد.
یک طراحی می تواند به این صورت باشد که شما یک کلاس Shape تعریف می کنید، که اشکال دیگر از آن کلاس به ارث می برند، و یک کلاس دیگر مثلا با نام DrawManager تعریف می کنید که لیست اشیاء را نگهداری می کنید و مسئول رسم اشکال مختلف بر روی صفحه نمایش است. مثلا متدی یا نام DrawAllShape داریم که با توجه به هر نوع هر شکل تابع مخصوص رسم ان شکل را فراخوانی می کند. در این متد ما کدی شبیه کد پایین خواهیم داشت.
If TypeOf S Is Circle Then DrawCircle() ElseIf TypeOf S Is Triangle Then DrawTriangle() Elseif … End If
|
بعد از مدتی نیاز پیدا می کنیم که اشکال جدید را به برنامه اضافه کنیم. برای اینکار ما کلاس های جدید را به برنامه اضافه می کنیم، و می رسیم به نقطه منفی کار، ما باید در کلاس DrawManager تغییرات انجام دهیم، متدهای جدیدی را باید به این کلاس اضافه کنیم تا رسم اشکال جدید را انجام دهد و کد متد DrawAllShape را تغییر دهیم تا در صورت وجود شکل های جدید، آنها را رسم کند. بعد از مدت متد DrawAllShape پر از جملات شرطی خواهد بود و همچنین تعداد زیادی متد خواهیم داشت که مدیریت و تغییر این کلاس را مشکل خواهد کرد.
حالا تصور کنید که کلاس انتزاعی یا یک اینترفیس به نام Shape تعریف کرده ایم که دارای یک متد به نام Draw می باشد، و تمام اشکال این متد را به ارث می براند و متد Draw را بازنویسی می کنند. در این حالت هر شکلی جدیدی به برنامه اضافه شود، کلاس shape را به ارث می برد و متد Draw را بازنویسی می کند و نیازی نیست که در کلاس DrawManager تغییری ایجاد کنم و این کلاس، تنها متد Draw هر شکل را برای رسم آن شکل فراخوانی می کند. همانطوریکه مشاهده کردید ما در این حالت در کد مربوط به هیچ یک از کلاس های موجود تغییری ایجاد نکردیم اما برنامه خود را توسعه دادیم.
در بسیاری از الگوهای طراحی یکی از مهمترین ویژگیهای که در نظر گرفته شده است، اصل open-closed می باشد. برای نمونه می توانید الگوی TEMPLATE را بررسی کنید. ما باید بعضی از تغییراتی که می تواند در سیستم رخ دهد را، پیش بینی کنیم و با استفاده از مفهوم abstractions از سیستم خود در مقابل این تغییرات حفاظت کنیم. در واقع می توان گفت که کلید اصل open-closed دو مفهوم abstractions و encapsulation می باشد.