وارونگی کنترل یک اصل طراحی شی گرایی است که در آن کنترل ماژولهای نرم افزار در مقایسه با برنامه نویسی رویه ای سنتی بالعکس می شود. به عنوان مثال فرض کنید شما می خواهید در محل کارخود کاری را انجام دهید و برای انجام کار باید با  ماشین رانندگی نیز انجام دهید. باکمک IOC شما از یک راننده استفاده می کنید تا برای شما کار رانندگی را انجام دهد و شما روی کار خود تمرکز می نمایید. این اصل برای افزایش ماژولار بودن برنامه (پیمانه ای بودن) و انعطاف پذیری آن و از بین بردن به هم پیوستگی شدید (tightly coupled) و رسیدن به کمترین میزان وابستگی (loosely coupled) می باشد.

اگر ما فرض کنیم که طبق شکل زیر کلاسی داریم که قرار است از دو سرویس A و B استفاده کند در نتیجه کلاس ما دارایوابستگی شدید به دو سرویس است و مشکلات زیر به وجود می آید:

وارونگی کنترل

اجازه دهید تا در اینجا نحوه تغییر جریان کنترل را به شیوه های مختلف توسط IOC را بهتر شرحدهیم:

کنترل روی جریان یک برنامه

در یک برنامه کنسولی سی شارپ اجرا از تابع main() آغاز می شود و کنترل جریان برنامه با ترتیب اجرای دستورات توسط آن صورت می گیرد. به مثال زیر در این رابطه نگاه کنید:

namespace FlowControlDemo
{
    class Program
    {
        static void Main(string[] args)
        {
           bool continueExecution = true;
            do
            {
                Console.Write("Enter First Name:");
                var firstName = Console.ReadLine();

                Console.Write("Enter Last Name:");
                var lastName = Console.ReadLine();

                Console.Write("Do you want to save it? Y/N: ");

                var wantToSave = Console.ReadLine();

                if (wantToSave.ToUpper() == "Y")
                    SaveToDB(firstName, lastName);

                Console.Write("Do you want to exit? Y/N: ");

                var wantToExit = Console.ReadLine();

                if (wantToExit.ToUpper() == "Y")
                    continueExecution = false;

            }while (continueExecution);
         
        }

        private static void SaveToDB(string firstName, string lastName)
        {
            //save firstName and lastName to the database here..
        }
    }
}

در این شبه کد با ورود نام و نام خانوادگی توسط کاربر، اطلاعات توسط تابع main() ذخیره شده و در صورت تمایل کاربر برنامه پایان می یابد. این یک نمونه ساده از پیاده سازی IOC روی جریان برنامه بود.

کنترل روی ایجاد ماژول یاشیء وابسته

ابتدا اجازه دهید که معنی وابستگی را بهتر شرح دهیم. به مثال زیر توجه کنید:

public class A
{
    B b;

    public A()
    {
        b = new B();
    }

    public void Task1() {
        // do something here..
        b.SomeMethod();
        // do something here..
    }

}

public class B {

    public void SomeMethod() { 
        //doing something..
    }
}

در مثال بالا کلاس A تابع b.SomeMethod() از کلاس B را برای تکمیل وظیفه خود صدا می زند. کلاس A بدون کلاس B قادر به تکمیل کار خود نمی باشد بنابراین ما می توانیم بگوییم کلاس A به کلاس B وابسته است و کلاس B ازوابستگی های کلاس A می باشد.

در طراحی شی گرا کلاسها می توانند با همدیگر تعامل داشته باشند و مدیریت چرخه زندکی یکدیگر را بر عهده بگیرند مانند بالا که مدیریت چرخه زندگی کلاس B به دست کلاس A می باشد.

IOC می تواند کنترل ایجاد کلاس وابسته را وارون نماید و کنترل لیجاد را در مثال بالا از کلاس A به کلاس دیگر بدهد.

public class A
{
    B b;

    public A()
    {
        b = Factory.GetObjectOfB ();
    }

    public void Task1() {
        // do something here..
        b.SomeMethod();
        // do something here..
    }
}

public class Factory
{
    public static B GetObjectOfB() 
    {
        return new B();
    }
}

همانطور که در شبه کد بالا دیده می شود، کلاس A از کلاس Factory برای ایجاد یک نمونه از کلاس B استفاده می نماید. بنابراین، ما کنترل ایجاد کلاس وابسته را از کلاس A به کلاس Factory داده ایم. برای درک بهتر این موضوع اجازه دهید یک مثال دیگر بزنیم. در طراحی شی گرایی کلاسها باید به صورت loosely coupled طراحی شوند و این بدان معناست که تغییرات روی یک کلاس نباید باعث تغییر در دیکر کلاسها شود. به شکل زیر توجه کنید:

وارونگی کنترل

شکل بالا الگوی طراحی لایه ای نرم افزار را نشان می دهد. در این الگو رابط کاربری از لایه سرویس برای واکشی و ذخیره اطلاعات استفاده می کند. لایه سرویس نیز خود از لایه منطق تجاری برای اعمال نقش ها و قوانین موجود در آن روی داده کمک می برد. و در نهایت لایه منطق تجاری نیز از لایه DataAccess برای ذخیره و بازیابی اطلاعات در پایگاه داده استفاده می نماید. در اینجا ما روی دو لایه BusinessLogic و DataAccess برای درک IOC تمرکز می نماییم.

در زیر شبه کد مربوط به این دو لایه (کلاس) برای موجودیت مشتری آورده شده است:

public class CustomerBusinessLogic
{
    DataAccess _dataAccess;

    public CustomerBusinessLogic()
    {
        _dataAccess = new DataAccess();
    }

    public string GetCustomerName(int id)
    {
        return _dataAccess.GetCustomerName(id);
    }
}

public class DataAccess
{
    public DataAccess()
    {
    }

    public string GetCustomerName(int id) {
        return "Dummy Customer Name"; // get it from DB in real app
    }
}

همانطور که در مثال بالا می بینید کلاس CustomerBusinessLogic به کلاس DataAccess وابسته است. این کلاس برای دریافت اطلاعات مشتری یک نمونه از کلاس DataAccess را ایجاد می کند. حالا ببینیم چه مشکلاتی در این مدل پیش می آید:

در این مثال دو کلاس DataAccess و CustomerBusinessLogic از نوع وابسته (tightly coupled) هستند زیرا کلاس CustomerBusinessLogic دارای یک نمونه از کلاس DataAccess در خود است که کنترل چرخه زندگی آنرا نیز در دست داردو مشکلات زیر پیش می آید:

بنابراین راه کار رفع نقایص بالا پیاده سازی اصل IOC در برنامه می باشد.

وارونگی کنترل

این اصل برای پیاده سازی دارای تکنیک های مختلف است که در زیر لیست آنها آمده است:

به طور مثال برای پیاده سازی الگوی IOC در کد بالا از روش Factory Pattern استفاده می کنیم و در گام اول یک کلاس Factory که یک نمونه از کلاس

public class DataAccessFactory
{
    public static DataAccess GetDataAccessObj() 
    {
        return new DataAccess();
    }
}

و در ادامه از این کلاس در کلاس CustomerBusinessLogic استفاده می نماییم.

    public CustomerBusinessLogic()
    {
    }

    public string GetCustomerName(int id)
    {
        DataAccess _dataAccess =  DataAccessFactory.GetDataAccessObj();

        return _dataAccess.GetCustomerName(id);
    }
}

همانطور که می بینید کلاس CustomerBusinessLogic از متد DataAccessFactory.GetDataAccessObj() برای داشتن یک نمونه از کلاس DataAccess به جای استفاده از کلمه کلیدی new برای ایجاد آن استفاده می کند. بنابراین ما کنترل ایجاد کلاس را از CustomerBusinessLogic به کلاس DataAccessFactory اعطا نمودیم