Programování

Jak používat inverzi řízení v C #

Inverze ovládání i vkládání závislostí vám umožňují prolomit závislosti mezi komponentami ve vaší aplikaci a usnadnit testování a údržbu vaší aplikace. Inverze ovládání a vkládání závislostí však nejsou stejné - mezi nimi jsou jemné rozdíly.

V tomto článku prozkoumáme inverzi vzorů ovládání a pochopíme, jak se liší od vkládání závislostí pomocí příslušných příkladů kódu v C #.

Chcete-li pracovat s příklady kódu uvedenými v tomto článku, měli byste mít ve svém systému nainstalovanou Visual Studio 2019. Pokud ještě nemáte kopii, můžete si stáhnout Visual Studio 2019 zde.

Vytvořte projekt konzolové aplikace v sadě Visual Studio

Nejprve si vytvořme projekt konzolové aplikace .NET Core v sadě Visual Studio. Za předpokladu, že je ve vašem systému nainstalovaná sada Visual Studio 2019, postupujte podle níže uvedených kroků a vytvořte nový projekt konzolové aplikace .NET Core v sadě Visual Studio.

  1. Spusťte Visual Studio IDE.
  2. Klikněte na „Vytvořit nový projekt“.
  3. V okně „Vytvořit nový projekt“ vyberte ze zobrazeného seznamu šablon „Console App (.NET Core)“.
  4. Klikněte na Další.
  5. V dalším okně „Konfigurace nového projektu“ zadejte název a umístění nového projektu.
  6. Klikněte na Vytvořit.

Tím se vytvoří nový projekt aplikace konzoly .NET Core v sadě Visual Studio 2019. Tento projekt použijeme k prozkoumání inverze ovládacího prvku v následujících částech tohoto článku.

Co je inverze kontroly?

Inverze řízení (IoC) je návrhový vzor, ​​ve kterém je invertován řídicí tok programu. Můžete využít inverze vzoru ovládacího prvku k oddělení komponent vaší aplikace, výměně implementací závislostí, falešným závislostem a vytvoření aplikace modulární a testovatelné.

Vkládání závislostí je podmnožinou principu inverze řízení. Jinými slovy, vkládání závislostí je jen jedním ze způsobů implementace inverze řízení. Můžete také implementovat inverzi řízení pomocí událostí, delegátů, vzoru šablony, tovární metody nebo vyhledávače služeb.

Inverze vzoru návrhu ovládacího prvku uvádí, že objekty by neměly vytvářet objekty, na kterých jsou závislé při provádění určité činnosti. Místo toho by tyto objekty měly získat z vnější služby nebo kontejneru. Myšlenka je obdobou hollywoodského principu, který říká: „Nevolej nám, zavoláme ti.“ Jako příklad namísto aplikace, která volá metody v rámci, by rozhraní volalo implementaci, která byla poskytnuta aplikací.

Příklad inverze ovládacího prvku v C #

Předpokládejme, že vytváříte aplikaci pro zpracování objednávek a chcete implementovat protokolování. Pro zjednodušení předpokládejme, že cílem protokolu je textový soubor. Vyberte projekt konzolové aplikace, který jste právě vytvořili v okně Průzkumníka řešení, a vytvořte dva soubory s názvem ProductService.cs a FileLogger.cs.

  veřejná třída ProductService

    {

soukromé pouze pro čtení FileLogger _fileLogger = nový FileLogger ();

public void Log (řetězcová zpráva)

        {

_fileLogger.Log (zpráva);

        }

    }

veřejná třída FileLogger

    {

public void Log (řetězcová zpráva)

        {

Console.WriteLine ("metoda Inside Log FileLogger.");

LogToFile (zpráva);

        }

private void LogToFile (string message)

        {

Console.WriteLine ("Metoda: LogToFile, Text: {0}", zpráva);

        }

    }

Implementace uvedená v předchozím fragmentu kódu je správná, ale existuje omezení. Jste omezeni na protokolování dat pouze do textového souboru. Data v žádném případě nemůžete protokolovat do jiných zdrojů dat nebo do různých cílů protokolu.

Nepružná implementace protokolování

Co kdybyste chtěli přihlásit data do databázové tabulky? Stávající implementace by to nepodporovala a vy byste byli nuceni implementaci změnit. Můžete změnit implementaci třídy FileLogger nebo můžete vytvořit novou třídu, řekněme DatabaseLogger.

    veřejná třída DatabaseLogger

    {

public void Log (řetězcová zpráva)

        {

Console.WriteLine ("metoda Inside Log z DatabaseLogger.");

LogToDatabase (zpráva);

        }

private void LogToDatabase (textová zpráva)

        {

Console.WriteLine ("Metoda: LogToDatabase, Text: {0}", zpráva);

        }

    }

Můžete dokonce vytvořit instanci třídy DatabaseLogger třídy uvnitř třídy ProductService, jak je ukázáno v fragmentu kódu níže.

veřejná třída ProductService

    {

soukromé pouze pro čtení FileLogger _fileLogger = nový FileLogger ();

soukromé pouze pro čtení DatabaseLogger _databaseLogger =

new DatabaseLogger ();

public void LogToFile (řetězcová zpráva)

        {

_fileLogger.Log (zpráva);

        }

public void LogToDatabase (řetězcová zpráva)

        {

_fileLogger.Log (zpráva);

        }

    }

Ačkoli by to fungovalo, co kdybyste potřebovali přihlásit data své aplikace do EventLogu? Váš návrh není flexibilní a budete nuceni změnit třídu ProductService pokaždé, když se budete muset přihlásit k novému cíli protokolu. To je nejen těžkopádné, ale také vám bude extrémně obtížné spravovat třídu ProductService v průběhu času.

Přidejte flexibilitu pomocí rozhraní

Řešením tohoto problému je použití rozhraní, které by implementovaly třídy konkrétního protokolovacího nástroje. Následující fragment kódu ukazuje rozhraní s názvem ILogger. Toto rozhraní by bylo implementováno dvěma konkrétními třídami FileLogger a DatabaseLogger.

veřejné rozhraní ILogger

{

void Log (řetězcová zpráva);

}

Aktualizované verze tříd FileLogger a DatabaseLogger jsou uvedeny níže.

veřejná třída FileLogger: ILogger

    {

public void Log (řetězcová zpráva)

        {

Console.WriteLine ("metoda Inside Log FileLogger.");

LogToFile (zpráva);

        }

private void LogToFile (string message)

        {

Console.WriteLine ("Metoda: LogToFile, Text: {0}", zpráva);

        }

    }

veřejná třída DatabaseLogger: ILogger

    {

public void Log (řetězcová zpráva)

        {

Console.WriteLine ("metoda Inside Log z DatabaseLogger.");

LogToDatabase (zpráva);

        }

private void LogToDatabase (textová zpráva)

        {

Console.WriteLine ("Metoda: LogToDatabase, Text: {0}", zpráva);

        }

    }

Nyní můžete použít nebo změnit konkrétní implementaci rozhraní ILogger, kdykoli je to nutné. Následující fragment kódu ukazuje třídu ProductService s implementací metody Log.

veřejná třída ProductService

    {

public void Log (řetězcová zpráva)

        {

ILogger logger = nový FileLogger ();

logger.Log (zpráva);

        }

    }

Zatím je vše dobré. Co když byste však chtěli použít DatabaseLogger namísto FileLogger v metodě Log třídy ProductService? Implementaci metody Log ve třídě ProductService můžete změnit tak, aby splňovala požadavek, ale nedělá to flexibilní design. Pojďme nyní učinit návrh flexibilnějším pomocí inverze řízení a vkládání závislostí.

Invertujte ovládací prvek pomocí vkládání závislostí

Následující fragment kódu ukazuje, jak můžete využít výhod injekce závislostí k předání instance třídy konkrétního protokolovacího nástroje pomocí injektoru konstruktoru.

veřejná třída ProductService

    {

soukromé jen pro čtení ILogger _logger;

public ProductService (ILogger logger)

        {

_logger = záznamník;

        }

public void Log (řetězcová zpráva)

        {

_logger.Log (zpráva);

        }

    }

Na závěr se podívejme, jak můžeme předat implementaci rozhraní ILogger do třídy ProductService. Následující fragment kódu ukazuje, jak můžete vytvořit instanci třídy FileLogger a použít předání závislosti pomocí injekce konstruktoru.

static void Main (řetězec [] args)

{

ILogger logger = nový FileLogger ();

ProductService productService = nový ProductService (logger);

productService.Log ("Hello World!");

}

Přitom jsme převrátili ovládání. Třída ProductService již není odpovědná za vytvoření instance implementace rozhraní ILogger nebo dokonce za rozhodování, která implementace rozhraní ILogger by měla být použita.

Inverze ovládání a vkládání závislostí vám pomohou s automatickým vytvářením instancí a správou životního cyklu vašich objektů. ASP.NET Core obsahuje jednoduchou integrovanou inverzi ovládacího kontejneru s omezenou sadou funkcí. Tento integrovaný kontejner IoC můžete použít, pokud jsou vaše potřeby jednoduché, nebo použít kontejner jiného výrobce, pokud chcete využít další funkce.

Další informace o tom, jak pracovat s inverzí ovládání a vkládáním závislostí v ASP.NET Core, najdete v mém dřívějším příspěvku zde.

$config[zx-auto] not found$config[zx-overlay] not found