در برنامه نویسی شی گرا، اصل Open Closed می گوید: "موجودات نرم افزاری (کلاس ها، ماژول ها، توابع و غیره) باید برای توسعه باز باشند، اما برای اصلاح بسته باشند.این بدان معناست که اگر موجودیتی مانند کلاس از قبل به خوبی تعریف شده باشد، نباید اصلاح شود.
معمولاً هنگامی که یک عملکرد جدید به یک برنامه اضافه می شود، تغییرات زیادی ایجاد می شود. این تغییرات در کد موجود باید به حداقل برسد.
نکته : اصل Open Closed بیان می کند که طراحی و نوشتن کد باید به گونه ای انجام شود که با حداقل تغییرات در کد موجود، قابلیت های جدید اضافه شود چرا که تغییر رفتار فعلی یک کلاس ممکن است روی تمامی قسمتهایی که از آن کلاس استفاده می کنند تاثیر بگذارد. طراحی باید به گونه ای انجام شود که امکان افزودن قابلیت های جدید به عنوان کلاس های جدید را فراهم کند و تا حد امکان کد موجود را بدون تغییر نگه دارد.
پیاده سازی (Implementation)
// Does not follow OCP public double Area(object[] shapes) { double area = 0; foreach (var shape in shapes) { if (shape is Rectangle) { Rectangle rectangle = (Rectangle) shape; area += rectangle.Width*rectangle.Height; } else { Circle circle = (Circle)shape; area += circle.Radius * circle.Radius * Math.PI; } } return area; } public class AreaCalculator { public double Area(Rectangle[] shapes) { double area = 0; foreach (var shape in shapes) { area += shape.Width*shape.Height; } return area; } }
این برنامه از OCP پیروی نمی کند زیرا Area() برای توسعه باز نیست و فقط می تواند اشکال مستطیل و دایره را مدیریت کند. اگر بخواهیم محاسباتی را برای مثلث(Triangle) اضافه کنیم، باید روش را اصلاح کنیم، بنابراین برای اصلاح بسته نیست.
ما میتوانیم با افزودن یک کلاس انتزاعی Shape که همه انواع شکلها به ارث میبرند، به OCP برسیم.
بیایید کد فوق را Refactor کنیم
public abstract class Shape { public abstract double Area(); } public class Rectangle : Shape { public double Width { get; set; } public double Height { get; set; } public override double Area() { return Width*Height; } } public class Circle : Shape { public double Radius { get; set; } public override double Area() { return Radius*Radius*Math.PI; } } public double Area(Shape[] shapes) { double area = 0; foreach (var shape in shapes) { area += shape.Area(); } return area; }
اکنون هر زیرگروه از shape از طریق چندریختی(polymorphism) محاسبه مساحت خود را انجام می دهد. این کار کلاس Shape را به توسعه باز می کند زیرا یک شکل جدید را می توان به راحتی با محاسبه مساحت خودش بدون خطا اضافه کرد.
علاوه بر این، هیچ چیز در برنامه Shape اصلی را تغییر نمی دهد و در آینده نیازی به اصلاح نخواهد داشت. در نتیجه، برنامه اکنون به اصل OCP دست می یابد.
نکته : باید با آینده نگری سطح صحیحی از انتزاع(abstractation) را محاسبه و انتخاب نمائیم.
اما چالش واقعی پیش بینی است، پیش بینی سخت است. اگر پیش بینی آسان بود، همه ما میلیاردر بیت کوین می شدیم!!!
نتیجه (Conclusion)
اگرچه اتخاذ اصل OCP ارزشی برای توسعه نرم افزار به ارمغان می آورد، با این حال، پیروی از این اصل ساده و آسان نیست. این امر مستلزم این است که برنامه نویس دارای آینده نگری بی نهایت باشد. باید الزاماتی را که ممکن است در آینده تغییر کنند پیش بینی کرد و اجزای آن را طوری طراحی کرد که در صورت اضافه شدن عملکرد جدید، نیازی به تغییر کد نباشد. به این ترتیب، این ممکن است باعث ایجاد سربار غیر ضروری به خصوص زمانی که نیازها دائماً در حال تغییر هستند، شود. در نهایت، این وظیفه بر عهده برنامه نویس است که قضاوت کند که آیا اصلاح کد موجود ضروری است یا خیر.
نکته : مانند هر اصل OCP فقط یک اصل است. ایجاد یک طراحی انعطاف پذیر مستلزم صرف زمان و تلاش اضافی برای آن است و سطح جدیدی از انتزاع را معرفی می کند که پیچیدگی کد را افزایش می دهد. بنابراین این اصل باید در حوزه هایی اعمال شود که احتمال تغییر وجود دارد.
نکته : در هر صورت، اصل KISS صدق می کند (Keep It Simple Stupid): دشواری پیش بینی را دست کم نگیرید و منابع خود را برای ایجاد انتزاعیاتی که به آن نیاز ندارید هدر ندهید.
الگوهای طراحی زیادی وجود دارد که به ما کمک می کند تا کد را بدون تغییر آن گسترش دهیم. به عنوان مثال الگوی Decorator به ما کمک می کند تا از اصل Open Closed پیروی کنیم. همچنین میتوان از Factory Method یا الگوی Observer برای طراحی برنامهای استفاده کرد که به راحتی با حداقل تغییرات در کد موجود قابل تغییر است.
بیشتر بخوانید : اصول طراحی نرم افزار Software Design Principles