Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Teil 2: Umkehrung der Kontrolle erkärt, oder: Von der Fabrikmethode zum DI-Container

Dies ist die Fortsetzung der Artikelserie "Umkehrung der Kontrolle erkärt, oder: Von der Fabrikmethode zum DI-Container" mit folgenden Teilen:

Der erste Teil liegt schon eine Zeit zurück, das hindert mich aber nicht daran, da doch noch mehr zu veröffentlichen.

Ein Rückblick auf Teil 1:

Wir haben Abhängigkeiten angeschaut, worin die Probleme liegen wie sie mithilfe der Umkehrung der Kontrolle mittels Factory Methode umgangen werden können.

Es gibt noch das Abstract Factory Pattern, von der Gang of Four folgendermassen beschrieben:

Bietet eine Schnittstelle zum Erzeugen von Familien verwandter oder voneinander abhängiger Objekte, ohne ihre konkreten Klassen zu benennen.

Auf folgender Abbildung ist gut zu erkennen, was für Akteure mitspielen:

  • Eine Schnittstelle Abstract Factory, die nur die Interfaces der Produkte zurückgibt.
  • N konkrete Implementationen wie z.B. Concrete Factory A.
  • Implementationen von den Produkten

Dieses Pattern erinnert mich an das Provider Pattern, wo es einen abstrakten Provider und bspw. FileProvider und SqlProvider gibt.
Einfach das in diesem Fall hier Objekte in verschiedenen Familien erzeugt werden, anstelle ein Verhalten in verschiedenen Familien zu repräsentieren.

Ich habe es in dieser Form noch nie genutzt aber es gibt sicher Einsatzfälle und Abwandlungen davon.

Das war jetzt ein kleiner Ausflug in die Patterns, aber eigentlich ging es darum, den Weg von der Fabrikmethode zum DI-Container zu beschreiben.

Eine Fabrikmethode ist das Mittel des DI-Containers, der dafür gedacht ist das schreiben und verwalten von vielen Factorymethoden zu ersparen. Er stellt eine globale Factorymethode dar, die über den Interfacetyp eine konkrete Klasse zurückgibt, die im Container registriert ist.

Er geht noch weiter indem er ganze Objekthierarchien von Abhängigkeiten in Form von Konstruktorargumenten oder Eigenschaften auflösen kann.

Diese gewonnene Bequemlichkeit forciert auch das Unterteilen von Code in mehrere Klassen, die jeweils dedizierte Aufgaben haben und dann per Container injiziert werden können.

 

Ich habe länger überlegt, was ich noch in diesen Artikel bringen soll. Es gibt sehr viele kleine, simple, einfache Beispiele für einen Container.
Zum Abschluss gibt es noch den Quellcode des Vorgängers von LightCore, der schon einiges konnte aber viel viel kleiner war als die aktuelle Version von LightCore.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using PeterBucher.AutoFunc.Fluent;
using PeterBucher.AutoFunc.Mapping;
namespace PeterBucher.AutoFunc
{
    public class AutoFuncContainer : IContainer
    {
        private readonly IDictionary<Type, IMappingItem> _mappings;
 
        public AutoFuncContainer()
        {
            this._mappings = new Dictionary<Type, IMappingItem>();
        }
 
        public ILifecycleFluent Register<TContract, TImplementation>()
        {
            Type typeOfContract = typeof (TContract);
            MappingItem mappingItem;
 
            if (!this._mappings.ContainsKey(typeOfContract))
            {
                Type typeofImplementation = typeof (TImplementation);
                mappingItem = new MappingItem(typeofImplementation);
                this._mappings.Add(typeOfContract, mappingItem);
                return mappingItem.LifecycleFluent;
            }
 
            throw new Exception("mapping for this contract allready registered");
        }
 
        private object Resolve(Type typeOfContract)
        {
            IMappingItem mappingItem = _mappings.Where(m => m.Key.Equals(typeOfContract)).SingleOrDefault().Value;
 
            switch (mappingItem.Lifecycle)
            {
                case Lifecycle.Singleton:
                    if (mappingItem.Instance == null)
                    {
                        this.CreateInstanceFromType(mappingItem.ImplementationType);
                    }
                    return mappingItem.Instance;
            }
 
            return this.CreateInstanceFromType(mappingItem.ImplementationType);
        }
 
        public TContract Resolve<TContract>()
        {
            return (TContract) this.Resolve(typeof (TContract));
        }
 
        private object CreateInstanceFromType(Type implementationType)
        {
            ConstructorInfo[] constructors = implementationType.GetConstructors();
            if (constructors.Length == 0 || constructors.Length == 1 && constructors[0].GetParameters() == null)
            {
                return Activator.CreateInstance(implementationType);
            }
 
            ConstructorInfo constructorWithDependencies = constructors.OrderByDescending(
                delegate(ConstructorInfo c)
                    {
                        var parameters = c.GetParameters();
                        return parameters == null || parameters.Count() == 0;
                    }).First();
 
            List<object> parameterResults = new List<object>();
 
            foreach (var parameter in constructorWithDependencies.GetParameters())
            {
                parameterResults.Add(this.Resolve(parameter.ParameterType));
            }
 
            return constructorWithDependencies.Invoke(parameterResults.ToArray());
        }
    }
}

Veröffentlicht Dienstag, 13. November 2012 17:56 von Peter Bucher
Abgelegt unter: , , , ,

Kommentare

# Dependency Injection und ASP.NET MVC 4 Autofac

In annähernd jedem Projekt nutze ich seit 2008 Dependency Injection (DI) um Komponenten und Module lose

Dienstag, 5. Februar 2013 09:55 by Jürgen Gutsch
Anonyme Kommentare sind nicht zugelassen