ioc and .net
TRANSCRIPT
Hello World
public class HelloWorldClass{ public void PrintMessage() { Console.WriteLine("Hello World"); }}
Co je na tom špatně?
Nic, ale− je úzce svázáno s třídou Console− při změně způsobu výstupu musíme překódovat− nemožnost změny implementace za běhu− obtížná testovatelnost
Určitě se toho najde víc...
Co s tím?
Zamyslíme se Napíšeme do googlu dotaz „Hello World problem” Přečteme si nějakou knížku o programování Zkusíme si najít jinou práci
Přečteme si dobrou knížku o programování...
Inversion of Control
IoC není návrhový vzor, je to princip Přístup pro navrhování znovu-použitelných komponent Uvolnění vazeb mezi komponentami
− Komponenta se zaměřuje jen na svůj úkol− Komponenta má definované rozhraní− Pro svoji činnost může využívat jiné komponenty− Lze jí zaměnit za jinou se stejným rozhraním
Zjednodušuje testovatelnost
Což takhle nějaký příklad?
Delegates (callbacks)
public delegate void OutputService(String message);
public class HelloWorldClass{ public void PrintMessage(OutputService service) { service("Hello World"); }}
public class ConsoleOutputService{ public static void Write(String message)
{ Console.WriteLine(message); }}
HelloWorldClass helloWorld = new HelloWorldClass();helloWorld.PrintMessage( new OutputService(ConsoleOutputService.Write));
helloWorld.PrintMessage(ConsoleOutputService.Write);
helloWorld.PrintMessage( delegate(String message) { Console.WriteLine(message); });
helloWorld.PrintMessage(message => Console.WriteLine(message) );
Multicast delegate
OutputService outputService = new OutputService( delegate(String message) { /* Write to console */ });
outputService += new OutputService( delegate(String message) { /* Send email */ });
HelloWorldClass helloWorld = new HelloWorldClass();helloWorld.PrintMessage(outputService);
Events
Spouštěné na základě nějaké akce− např. stisknutí tlačítka nebo „tik” časovače
Podle konvence, události by měli mít 2 parametry:− sender – objekt, který událost posílá− argumenty – data související s událostí (dle konvence
by to měl být potomek třídy EventArgs) Hojně používáno v uživatelském rozhraní
public delegate void WriteMessageHandler( object sender, string message);
public class HelloWorldClass{ public event WriteMessageHandler WriteMessageEvent;
public void PrintMessage() { if (WriteMessageEvent != null) WriteMessageEvent(this, "Hello World"); }}
HelloWorldClass helloWorld = new HelloWorldClass();helloWorld.WriteMessageEvent += delegate(object sender, string message) { /* -1- */ };helloWorld.WriteMessageEvent += delegate(object sender, string message){ /* -2- */ };helloWorld.PrintMessage();
Asynchronous Delegates
Asynchronní delegáty lze volat pomocí BeginInvoke/EndInvoke
public class HelloWorldClass{ public void PrintMessage(OutputService service) { service.BeginInvoke("Hello World", null, null); }}
Predefined delegates
Delegáti nevracející hodnotu
public delegate void Action()public delegate void Action<in T>(T obj)
…
Delegáti vracející hodnotu
public delegate TResult Func<out Tresult>()public delegate TResult Func<in T, out TResult>(T arg)…
public delegate bool Predicate<in T>(T obj)
A jak to bude vypadat v HelloWorld?
public class HelloWorldClass{ public void PrintMessage(Action<String> service) { service("Hello World"); }}
HelloWorldClass helloWorld = new HelloWorldClass();helloWorld.PrintMessage( delegate(String message) { Console.WriteLine(message); });
Více o delegátech
Další téma na IT Pivo ;-) C# 3.0 Cookbook, Third Edition Chapter 9: Delegates, Events, and Lambda
Expressions
http://msdn.microsoft.com/en-us/library/orm-9780596516109-03-09.aspx
Interface
public interface IOutputService{ void Write(String message);}
public class ConsoleOutputService : IOutputService{ public void Write(string message) { Console.WriteLine(message); }}
Service locator
public class ServiceLocator{ public static IOutputService GetOutputService() { return new ConsoleOutputService(); }}
public class HelloWorldClass{ public void PrintMessage() { IoutputService service = ServiceLocator.GetOutputService(); service.Write("Hello World"); }}
Service locator
Klady Skrytí rozhraní od implementace Run-time záměna implementace
Zápory Singleton Pro rozsáhlé systémy obsahuje hodně metod, nebo se musí sepatovat do více tříd Komponenty mají závislost na service locator Horší testovatelnost
Dependency Injection
Asi nejpoužívanější vzor pro IoC Komponenta zná vnější svět jen dle rozhraní Implementace rozhraní dostává zvenčí
Způsoby nastavení implementací rozhraní− Constructor injection− Setter injection− Method injection− Interface injection
Constructor Injection
public class HelloWorldClass{ private IOutputService service;
public HelloWorldClass(IOutputService service) { this.service = service; }
public void PrintMessage() { service.Write("Hello World"); }}
Setter Injection
public class HelloWorldClass{ public IOutputService Service { get; set; }
public void PrintMessage() { Service.Write("Hello World"); }}
Method Injection - I
public class HelloWorldClass{ public void PrintMessage(IOutputService service) { service.Write("Hello World"); }}
Method Injection - II
public class HelloWorldClass{ private IOutputService service;
public void Init(IOutputService service) { this.service = service; }
public void PrintMessage() { service.Write("Hello World"); }}
Interface Injection
public interface IOutputServiceInitializer{ void Init(IOutputService service);}
public class HelloWorldClass : IOutputServiceInitializer{ private IOutputService service;
public void Init(IOutputService service) { this.service = service; } ...}
Dependency Injection
Klady Komponenta dělá jen svou práci Komponenta s okolím komunikuje pouze přes rozhraní Implementace rozhraní jsou dodávány za běhu Testovatelnost
Zápory Horší hledání integračních chyb Možný nižší výkon (injekce, přetypování...)
IoC Containers
Komponenta zjednodušující DI Konfigurace závislostí (kód, soubor...) Vytváření objektů Doplnění závislostí Spravuje živostnost objektů
− Always New− Container Lifetime− Thread Lifetime− Time Lifetime− Request / Session
A někteří dělají ještě více
Příklad práce s IoC Container
public class Container{ public void Register<T, TImpl>() { … } public T Resolve<T>() { … }}
Container container = new Container();container.Register<IOutputService, ConsoleOutputService>();container.Register<HelloWorldClass, HelloWorldClass>();
HelloWorldClass hello = container.Resolve<HelloWorldClass>();hello.PrintMessage();
Jaký způsob injection používá tento container?
Jaký IoC Container vybrat
Funkčnost Konfigurace Výkon Napsat si vlastní
http://www.palmmedia.de/Blog/2011/8/30/ioc-container-benchmark-performance-comparison
Tips
Používat DI a Constructor Injection Používat „vhodný” Container Neodkazovat z komponent na Container (pokud potřebuji
více instancí, nebo nové instance, použít delegáta)
The last example
public interface IA {}
public class B{ public B(Func<IA> getA, Action<IA> releaseA) { this.getA = getA; this.releaseA = releaseA; } public void Process() { IA a = getA(); // use a releaseA(a); }}