我熟悉这些模式,但仍然不知道如何处理以下情况:
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; } } }
通常,问题在于需要注入的引用数量。如果有更多的汽车,情况将会更糟。
我想到的第一种方法是在工厂构造函数中注入Car1和Car2,但是这与工厂方法相反,因为工厂将始终返回相同的对象。第二种方法是注入servicelocator,但是它到处都是反模式。怎么解决呢?
替代方法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; } } }
替代方法2(由于树中过多的依赖关系而很难使用):
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; } } }
在工厂内部有一个switch case语句是一种代码味道。有趣的是,您似乎根本没有专注于解决该问题。
针对这种情况的最佳,最直接的DI解决方案是策略模式。它允许您的DI容器将依赖项注入到它们所属的工厂实例中,而不会使具有这些依赖项的其他类变得混乱,也不必诉诸服务定位器。
public interface ICarFactory { ICar CreateCar(); bool AppliesTo(Type type); } public interface ICarStrategy { ICar CreateCar(Type type); }
public class Car1Factory : ICarFactory { private readonly IDep1 dep1; private readonly IDep2 dep2; private readonly IDep3 dep3; public Car1Factory(IDep1 dep1, IDep2 dep2, IDep3 dep3) { if (dep1 == null) throw new ArgumentNullException("dep1"); if (dep2 == null) throw new ArgumentNullException("dep2"); if (dep3 == null) throw new ArgumentNullException("dep3"); this.dep1 = dep1; this.dep2 = dep2; this.dep3 = dep3; } public ICar CreateCar() { return new Car1(this.dep1, this.dep2, this.dep3); } public bool AppliesTo(Type type) { return typeof(Car1).Equals(type); } } public class Car2Factory : ICarFactory { private readonly IDep4 dep4; private readonly IDep5 dep5; private readonly IDep6 dep6; public Car1Factory(IDep4 dep4, IDep5 dep5, IDep6 dep6) { if (dep4 == null) throw new ArgumentNullException("dep4"); if (dep5 == null) throw new ArgumentNullException("dep5"); if (dep6 == null) throw new ArgumentNullException("dep6"); this.dep4 = dep4; this.dep5 = dep5; this.dep6 = dep6; } public ICar CreateCar() { return new Car2(this.dep4, this.dep5, this.dep6); } public bool AppliesTo(Type type) { return typeof(Car2).Equals(type); } }
public class CarStrategy : ICarStrategy { private readonly ICarFactory[] carFactories; public CarStrategy(ICarFactory[] carFactories) { if (carFactories == null) throw new ArgumentNullException("carFactories"); this.carFactories = carFactories; } public ICar CreateCar(Type type) { var carFactory = this.carFactories .FirstOrDefault(factory => factory.AppliesTo(type)); if (carFactory == null) { throw new Exception("type not registered"); } return carFactory.CreateCar(); } }
// I am showing this in code, but you would normally // do this with your DI container in your composition // root, and the instance would be created by injecting // it somewhere. var strategy = new CarStrategy(new ICarFactory[] { new Car1Factory(dep1, dep2, dep3), new Car2Factory(dep4, dep5, dep6) }); // And then once it is injected, you would simply do this. // Note that you could use a magic string or some other // data type as the parameter if you prefer. var car1 = strategy.CreateCar(typeof(Car1)); var car2 = strategy.CreateCar(typeof(Car2));
请注意,由于没有switch case语句,因此可以在不更改设计的情况下将其他工厂添加到策略中,并且这些工厂中的每个工厂都可以具有自己的依赖性,这些依赖性由DI容器注入。
var strategy = new CarStrategy(new ICarFactory[] { new Car1Factory(dep1, dep2, dep3), new Car2Factory(dep4, dep5, dep6), new Car3Factory(dep7, dep8, dep9) }); var car1 = strategy.CreateCar(typeof(Car1)); var car2 = strategy.CreateCar(typeof(Car2)); var car3 = strategy.CreateCar(typeof(Car3));