Ein Dependency Injection (DI) Container ist genau das. Ein Framework zur Erleichterung von DI. Sie übergeben den Container nicht, um Instanzen von Objekten aufzulösen. Sie fordern nur den Typ an, den Sie in Ihrem Klassenkonstruktor benötigen, und das DI-Framework injiziert die entsprechende Abhängigkeit.
Mark Seemann hat eine gute book on dependency injection geschrieben, die ich empfehlen würde.
Sie registrieren alles, was mit dem Container im Kompositionsstamm aufgelöst werden muss. Das heißt, wenn Ihr Programm startet, sollte alles registriert werden.
Lassen Sie uns sagen, dass wir den folgenden Code haben:
public class MyClass
{
public Run()
{
var dependency = new Dependency1();
dependency.DoSomething();
}
}
public class Dependency1
{
public void DoSomething()
{
var dependency = new Dependency2();
dependeny.DoSomethingElse();
}
}
public class Dependency2
{
public void DoSomethingElse()
{
}
}
Dies gibt uns die oben Abhängigkeitskette: MyClass -> abhaengigkeit1 -> Dependency2.
Das erste, was wir tun sollten, ist Refactoring der Klassen ihre Abhängigkeiten durch ihren Konstruktor zu nehmen und auf Schnittstellen statt Konkretionen beruhen. Wir können keine Abhängigkeiten einfügen, es sei denn, es gibt einen Platz, um sie zu injizieren (Konstruktor, Eigenschaft usw.). Hier
ist die Überarbeitete Code:
public interface IMyClass
{
void Run();
}
public interface IDependency1
{
void DoSomething();
}
public interface IDependency2
{
void DoSomethingElse();
}
public class MyClass : IMyClass
{
public readonly IDependency1 dep;
public MyClass(IDependency1 dep)
{
this.dep = dep;
}
public void Run()
{
this.dep.DoSomething();
}
}
public class Dependency1 : IDependency1
{
public readonly IDependency2 dep;
public MyClass(IDependency2 dep)
{
this.dep = dep;
}
public void DoSomething()
{
this.dep.DoSomethingElse();
}
}
public class Dependency2 : IDependency2
{
public void DoSomethingElse()
{
}
}
Hier finden Sie die Klassen bemerken jetzt alle nehmen ihre Abhängigkeiten durch ihre Konstrukteure und nicht neu bis nichts. Klassen sollten nur Abhängigkeiten aufnehmen, die sie tatsächlich benötigen. Zum Beispiel benötigt MyClass keine Dependency2, so dass sie nicht nach einem fragt. Es fragt nur nach einer Abhängigkeit1, weil das alles ist, was es braucht. Abhängigkeit1 NEEDS Dependency2, nicht MyClass.
Jetzt alles auf, ohne einen Behälter verdrahten, wir würden in der Zusammensetzung Wurzel gerade neu es auf dem Punkt:
void Main()
{
var myClass = new MyClass(new Dependency1(new Dependency2()));
}
Sie sehen können, wie das cumbersom bekommen könnte, wenn wir Tonnen von Klassen und depdencies haben. Deshalb benutzen wir einen Container. Es behandelt alle Dependenz-Graphen für uns. Mit einem Behälter würden wir es wie folgt umschreiben:
void Main()
{
// the order of our registration does not matter.
var container = new Container();
container.Register<IDependency1>.For<Dependency1>();
container.Register<IDependency2>.For<Dependency2>();
container.Register<IMyClass>.For<MyClass>();
// then we request our first object like in the first example (MyClass);
var myClass = container.Resolve<IMyClass>();
myClass.Run();
}
Im zweiten Beispiel wird der Behälter entsprechend verdrahteten alle Abhängigkeiten behandeln. Daher müssen wir Depetency2 niemals zu MyClass und Depeption1 weiterleiten. Wir müssen es nur in Dependency1 anfordern und der Container wird es für uns wie im ersten Beispiel verdrahten.
So in Ihrem Beispiel würden wir es wie so umschreiben:
static void Main(string[] args)
{
var game = new UnityContainer();
game.RegisterType<IGame, TrivialPursuit>();
game.RegisterType<IAnotherClass, AnotherClass>();
game.RegisterType<IYetAnotherClass, YetAnotherClass>();
var gameInstance = game.Resolve<IGame>();
// you'll need to perform some action on gameInstance now, like gameInstance.RunGame() or whatever.
}
public class Game : IGame
{
public Game(IAnotherClass anotherClass)
{
}
}
public class AnotherClass : IAnotherClass
{
public AnotherClass(IYetAnotherClass yetAnotherClass)
{
}
}
public class YetAnotherClass : IYetAnotherClass {}
In diesen Fällen gibt es keine Notwendigkeit, den Behälter herum passieren. Sie registrieren Ihre Abhängigkeiten mit dem Container und fordern sie dann in Ihren Klassenkonstruktoren an. Wenn Sie den Container in der Klasse verwenden möchten, ohne ihn über den Konstruktor anzufordern, dann tun Sie nicht DI, sondern verwenden den Container nur als Singleton-Service-Locator. Etwas, das generell vermieden werden sollte.
Container als Service Locator Diese sollten generell vermieden werden, aber wenn Sie den Behälter als Service-Locator verwenden möchten, haben Sie zwei Möglichkeiten:
1) Führen Sie den Behälter in die Klassen, die sie benötigen, um durch der Konstruktor. Sie können die obigen Beispiele verwenden, um Ihre Klassen für DI zu verkabeln. Aber anstatt eine Abhängigkeit wie IDependency im Konstruktor anzufordern, übergeben Sie einfach den Container.
public class Game : IGame
{
public Game(IContainer container)
{
var blah = container.Resolve<IBlah>();
}
}
2) Fordern Sie Ihren Behälter durch eine statische Klasse:
public static class ServiceLocator
{
private static IContainer container;
public static IContainer Container
{
get
{
if (container == null)
{
container = new Container();
}
return container;
}
}
}
alles normal Registrieren Sie sich in Ihrer Komposition Wurzel der Servicelocator-Klasse. Dann zu verwenden:
public class MyClass
{
public void DoSomething()
{
var blah = ServiceLocator.Container.Resolve<IBlah>();
}
}
Welche Art von Anwendung entwickeln Sie? Netz? Desktop? –
Desktop (Konsolen-App), um es zu testen, und dann Web (MVC), sobald ich die Konzepte runter. – CodingRiot