[c#] Factory-Methode mit DI und IoC



2 Answers

Antworten Sie auf Ihren Kommentar zum Codebeispiel mit Composition Root . Sie können Folgendes erstellen und dies ist kein Service Locator.

public class CarFactory
{
    private readonly Func<Type, ICar> carFactory;

    public CarFactory(Func<Type, ICar> carFactory)
    {
       this.carFactory = carFactory;
    }

    public ICar CreateCar(Type carType)
    {
        return carFactory(carType);
 }

und so sehen Sie Ihren Composition Root mit dem Unity DI-Container aus:

Func<Type, ICar> carFactoryFunc = type => (ICar)container.Resolve(type);
container.RegisterInstance<CarFactory>(new CarFactory(carFactoryFunc));
Question

Ich kenne diese Muster, weiß aber nicht, wie ich mit der folgenden Situation umgehen soll:

public class CarFactory
{
     public CarFactory(Dep1,Dep2,Dep3,Dep4,Dep5,Dep6)
     {
     }

     public ICar CreateCar(type)
     {
            switch(type)
            {
               case A:
                   return new Car1(Dep1,Dep2,Dep3);
               break;

               case B:
                   return new Car2(Dep4,Dep5,Dep6);
               break;
            }
     }
}

Im Allgemeinen besteht das Problem in der Menge der Referenzen, die injiziert werden müssen. Es wird noch schlimmer, wenn es mehr Autos gibt.

Die erste Annäherung, die mir in den Sinn kommt, ist, Car1 und Car2 in den Fabrikbauer zu injizieren, aber es ist gegen den Fabrikansatz, weil die Fabrik immer dasselbe Objekt zurückgibt. Der zweite Ansatz besteht darin, servicelocator zu injizieren, aber es ist überall Antipattern. Wie man es löst?

Bearbeiten:

Alternativer Weg 1:

public class CarFactory
{
     public CarFactory(IContainer container)
     {
        _container = container;
     }

     public ICar CreateCar(type)
     {
            switch(type)
            {
               case A:
                   return _container.Resolve<ICar1>();
               break;

               case B:
                     return _container.Resolve<ICar2>();
               break;
            }
     }
}

Alternativer Weg 2 (zu schwer zu verwenden wegen zu vieler Abhängigkeiten im Baum):

public class CarFactory
{
     public CarFactory()
     {
     }

     public ICar CreateCar(type)
     {
            switch(type)
            {
               case A:
                   return new Car1(new Dep1(),new Dep2(new Dep683(),new Dep684()),....)
               break;

               case B:
                    return new Car2(new Dep4(),new Dep5(new Dep777(),new Dep684()),....)
               break;
            }
     }
}



Viele DI-Container unterstützen den Begriff der benannten Abhängigkeiten.

ZB (Strukturkartensyntax)

For<ICar>().Use<CarA>().Named("aCar");
Container.GetNamedInstance("aCar") // gives you a CarA instance

Wenn Sie etwas wie eine Konvention verwenden, eine Regel, wie der Name von dem konkreten Autotyp abgeleitet wird, haben Sie eine Situation, in der Sie die Fabrik nicht mehr berühren müssen, wenn Sie das System erweitern.

Die Verwendung in einer Fabrik ist unkompliziert.

class Factory(IContainer c) {
  public ICar GetCar(string name) {
    Return c.GetNamedInstance(name);
  }
}



Ich würde erwägen, den Abhängigkeiten eine gute Struktur zu geben, damit Sie etwas Ähnliches wie Wiktors Antwort verwenden können, aber ich würde die Autofabrik selbst abstrahieren. Dann verwenden Sie nicht die Struktur if..then.

public interface ICar
{
    string Make { get; set; }
    string ModelNumber { get; set; }
    IBody Body { get; set; }
    //IEngine Engine { get; set; }
    //More aspects...etc.
}

public interface IBody
{
    //IDoor DoorA { get; set; }
    //IDoor DoorB { get; set; }
    //etc
}

//Group the various specs
public interface IBodySpecs
{
    //int NumberOfDoors { get; set; }
    //int NumberOfWindows { get; set; }
    //string Color { get; set; }
}

public interface ICarSpecs
{
    IBodySpecs BodySpecs { get; set; }
    //IEngineSpecs EngineSpecs { get; set; }
    //etc.
}

public interface ICarFactory<TCar, TCarSpecs>
    where TCar : ICar
    where TCarSpecs : ICarSpecs
{
    //Async cause everything non-trivial should be IMHO!
    Task<TCar> CreateCar(TCarSpecs carSpecs);

    //Instead of having dependencies ctor-injected or method-injected
    //Now, you aren't dealing with complex overloads
    IService1 Service1 { get; set; }
    IBuilder1 Builder1 { get; set; }
}

public class BaseCar : ICar
{
    public string Make { get; set; }
    public string ModelNumber { get; set; }
    public IBody Body { get; set; }
    //public IEngine Engine { get; set; }
}

public class Van : BaseCar
{
    public string VanStyle { get; set; } 
    //etc.
}

public interface IVanSpecs : ICarSpecs
{
    string VanStyle { get; set; }
}

public class VanFactory : ICarFactory<Van, IVanSpecs>
{
    //Since you are talking of such a huge number of dependencies,
    //it may behoove you to properly categorize if they are car or 
    //car factory dependencies
    //These are injected in the factory itself
    public IBuilder1 Builder1 { get; set; }
    public IService1 Service1 { get; set; }

    public async Task<Van> CreateCar(IVanSpecs carSpecs)
    {
        var van = new Van()
        {
           //create the actual implementation here.
        };
        //await something or other
        return van;
    }
}

Ich habe es nicht aufgelistet, aber Sie können jetzt mehrere Arten von Autos und ihre entsprechenden Fabriken implementieren und DI verwenden, um zu injizieren, was auch immer Sie brauchen.






Related