‫یک مثال عملی از Decoupling

من قبلاً هم راجع به Decoupling یا افتراق در نرم‌افزار خیلی چیزها می‌دانستم و همیشه عاشق مزایای آن بودم و دوست داشتم هر کدی که می‌نویسم Decoupling آن در سطح بالایی باشد. اما همان طور که همه می‌دانند یاد گرفتن یک ایده کار خیلی سختی نیست ولی عملی کردن آن چرا.

امروز موقعیت خوبی در یک برنامه ASP.NET پیش آمد که هم مزایای واقعی Decoupling (افتراق) را درک کنم و هم یک مثال ساده از آن اجرا کنم. یکی از امکاناتی که برای قسمت بندی بخش‌های مختلف برنامه و امکان Code Reuse در ASP.NET فراهم شده است امکان User Control است. برنامه‌نویس می‌تواند یک User Control نوشته و آن را در چند جای مختلف استفاده کند. یا حتی صرفاً از آن برای کاهش پیچیدگی استفاده کند. یکی از کارهای رایجی که خودم به شخصه در User Controlها انجام می‌دهم ارتباط با کنترل والد یا همان کنترلی است که کنترل مورد نظر من در آن قرار دارد. در موردی که من امروز با آن برخورد کردم User Control به Parent خود دسترسی پیدا کرده و از طریق FindControl یکی از دکمه‌های آن را Disable/Enable می‌کرد. این موضوع به غیر نازیبایی و ناخوانایی که ایجاد کرده بود باعث شده بود وقتی از این کنترل در جای دیگری استفاده می‌کردم خطا به وجود بیاید چون آن دکمه دیگر در اینجا وجود نداشت.

اعمال Decoupling (افتراق) در اینجا خیلی ساده بود. در User Control مورد بحث یک Event تعریف کردم و Disable/Enable کردن دکمه مورد نظر را به خود ماژول استفاده کننده سپردم. این ماژول فقط در Event تعریف شده مشترک می‌شد و Disable/Enable کردن دکمه را خودش انجام می‌داد. ماژول‌های دیگری هم که از User Control من استفاده می‌کردند هم صرفاً از همین Event استفاده می‌کنند. در واقع دیگر لازم نیست که User Control مورد نظر اطلاعی از وضعیت ماژول‌های والد خود داشته باشد و این یعنی Decoupling (افتراق).

مشکل ارتقای اجزای نرم‌افزار

یکی از مشکلات بزرگی که نرم‌افزار نویس‌ها مدام با آن دسته و پنجه نرم می‌کنند مشکل ارتقا اجزا و بخش‌های مختلف یک نرم‌افزار است. برای این مشکل مثال‌های زیادی وجود دارند:
۱- برنامه را با VB6 نوشته‌اید ولی حالا که به VB.NET ارتقا داده‌اید متوجه شده‌اید که پیش‌فرض VB.NET برای اعضای کلاس private است نه public.
۲- برنامه را با NHibernate 1.2 نوشته‌اید ولی بعد از ارتقا به NHibernate 2.0 فهمیده‌اید که برنامه با آن configuration قدیمی کار نمی‌کند و باید web.config/app.config را هم عوض کنید.
۳- برنامه را با فلان library نوشته‌اید و بعد از ارتقا به نسخه جدید فهمیده‌اید که نسخه جدید به خیلی از مسائلی که قبلاً گیر نمی‌داد حالا گیر می‌دهد و نمی‌گذارد برنامه اجرا شود.
۴- در برنامه‌های ASP.NET از کنترل‌های Telerik استفاده کرده‌اید و بعد از ارتقا فهمیدید که tagهای استفاده شده در markup اسمشان عوض شده و حالا دچار parser error شده‌اید.
۵- در برنامه‌ای که برای ویندوز ۹۵ نوشته شده از یک API خاص استفاده شده که دیگر در ویندوزهای جدیدتر وجود ندارد.
۶- در بخشی از برنامه از نسخه x یک library استفاده کرده‌اید و بعداً لازم می‌شود بخشی از یک نرم‌افزار دیگر را به نرم‌افزار فعلی‌تان الحاق کنید اما این بخش جدید از نسخه y استفاده می‌کند نه x.
۷-…

این مسئله که برای برنامه‌نویسان عذاب‌آور و برای مدیران ترس‌آور است را می‌توان به دو بخش خارجی و داخلی تقسیم کرد. بخش خارجی مربوط است به dllها، libraryها، APIها و platformهایی که از خارج شرکت/تیم می‌آیند و معمولاً حق انتخاب زیادی در مورد آنها وجود ندارد. بخش داخلی مربوط است به اجزا و dllهایی که در داخل شرکت ساخته می‌شود و version زدن آنها یک مصیبت بزرگ است. در مورد dllهای داخلی مسئله backward comaptiblity و وابستگی به dllهای دیگر هم باید چک شود. چیزی که معمولاً در مورد dllهای خارجی بهتر رعایت می‌شود.

برای کاهش مشکلات ارتقا به نظر بنده موارد زیر را می‌توان در نظر گرفت:
۱- مثبت‌نگر باشید. ارتقا dllها خصوصاً در مورد dllهای خارجی معمولاً نوید دهنده امکانات بهتر و کامل‌تر است. معمولاً هدف از ارائه نسخه جدیدتر رفع اشکالات نسخ قبلی، بهبود عملکرد و… است. پس بنابراین خوشحال باشید که رفع مشکلات مربوط به نسخه جدید به معنی ارتقا کیفیت نرم‌افزار شما خواهد بود.
۲- پیروی از اصل KISS (Keep It Simple Stupid)‎: یعنی تا آنجا که می‌توانید برنامه را ساده نگه داشته و بیخودی از هر کتابخانه، class و dll دیگری استفاده نکنید.
۳- Dependency Injection: برنامه‌ها با حداقل وابستگی به هم نوشته شوند.
۴- موتوا قبل ان تموتوا (بمیرید قبل از آن که بمیرید): قبل از آن که مجبور شوید ارتقا دهید، ارتقا دهید. تولید کنندگان نرم‌افزار معمولاً قبل از ارائه نهایی نسخ جدید آنها را به صورت نسخه‌های آزمایشی ارائه می‌دهند و حتی قبل از آن کلی بحث راه می‌اندازند، نظر سنجی و اطلاع‌رسانی می‌کنند که ما قرار است فلان چیز را به نرم‌افزارمان اضافه کنیم یا تغییر بدهیم. بنابراین فرصت خیلی زیادی دارید که خود را به موقع ارتقا دهید.
۵- پیروی از تجارب TDD مثل Unit Test و Continuous integration: این راه‌حل خصوصاً در مورد dllهای داخلی خیلی خوب جواب می‌دهد. اگر برای همه بخش‌هایی ممکن unit test نوشته باشید آن وقت نگرانی کمتری برای ارتقا دارید چون خیلی سریع می‌فهیمد که آیا چیزی خراب شده یا نه. Continuous integration هم به همین شکل کمک می‌کند که در حداقل زمان ممکن خطا را کشف کنید.
۶- به هیچ وجه Warningهای هنگام کامپایل را دستکم نگیرید. خیلی از warningهای نسخه امروز به compile errorهای نسخه فردا تبدیل خواهند شد.
۷- استفاده از الگوهای جدیدی مثل MVC چون کار تست را راحت‌تر کرده و decoupling که با استفاده از dependency injection محقق می‌شود را بهتر اجرا می‌کنند.

شما چه راه‌هایی برای کاهش این طور مشکلات سراغ دارید؟