如何配置ninject以将不同的依赖类型注入同一个类?

时间:2016-11-03 15:33:14

标签: dependency-injection ninject inversion-of-control

我遇到了我认为必须是常见的依赖注入相关问题。我无法找到相关的例子,我不喜欢我能够提出的最佳解决方案。

public class WasherDryerFolderSystem : ILaundrySystem
{
    private IWasher _washer;
    private IDryer _dryer;
    private IFolder _folder;

    public WasherDryerFolderSystem(IWasher washer, IDryer dryer, IFolder folder)
    {...}

    public void DoLaundry()
    {
        _washer.Wash();
        _dryer.Dry();
        _folder.Fold();
    }
}


public class HandWasher : IWasher {...}
public class MachineWasher : IWasher {...}

public class HandDryer : IDryer {...}
public class MachineDryer : IDryer {...}

public class HandFolder : IFolder {...}
public class MachineFolder : IFolder {...}

现在在主应用程序中我有类似

的东西
var laundrySystem = _kernel.Get<ILaundrySystem>(someUserInput);

配置此类所需的绑定有什么好方法?这是迄今为止我能够想到的(我不喜欢):

Bind<ILaundrySystem>().To<WasherDryerFolderSystem>()
    .Named(MACHINEWASH_HANDDRY_HANDFOLD)
    .WithConstructorArgument("washer", new MachineWasher())
    .WithConstructorArgument("dryer", new HandDryer())
    .WithConstructorArgument("folder", new HandFolder());

起初我并不认为这看起来太糟糕,但是当洗衣机和干衣机和文件夹都有自己的依赖时,这很快就会变得难看。

这让我感觉它应该是一个常见问题,但我找不到任何有用的东西。我的设计不正确吗?

2 个答案:

答案 0 :(得分:0)

您可以使用工厂模式:

public interface ILaundrySystemFactory
{
    ILaundrySystem  Create(string someUserInput);
}

public class LaundrySystemFactory : ILaundrySystemFactory
{
    private readonly IKernel _kernel;

    public LaundrySystemFactory(IKernel kernel){
        _kernel = kernel;
    }

    public ILaundrySystem Create(string someUserInput)
    {
        if(someUserInput){
            var washer = _kernel.Get<MachineWasher>();
            var dryer = _kernel.Get<HandDryer>();
            var folder = _kernel.Get<HandFolder>();
        } else {            
            var washer = _kernel.Get<DifferentWasher>();
            var dryer = _kernel.Get<DifferentDryer>();
            var folder = _kernel.Get<DifferentFolder>();
        }
        return new WasherDryerFolderSystem(washer, dryer, folder);
    }
}

然后简单地

private readonly ILaundrySystemFactory  _laundrySystemFactory;

ctor(ILaundrySystemFactory laundrySystemFactory){
    _laundrySystemFactory = laundrySystemFactory;
}

public UserInputMethod(string someUserInput)
{
    var loundrySystem = laundrySystemFactory.Create(someUserInput);
    var loundry = loundrySystem.DoLaundry();
}

绑定:

Bind<ILaundrySystemFactory>().To<LaundrySystemFactory>();

(某些DI容器可能还需要以下内容:)

Bind<MachineWasher>().To<MachineWasher>();

答案 1 :(得分:0)

使用您需要的具体参数创建具体类,将它们作为策略的依赖项,将根据用户输入使用它们。接下来,使用SINGLE调用解析根类来实例化它们。 OFC策略可以是解析根本身,但它也可以是不同重新生成根的依赖。例如:

//DoLaundry based on user input
public class WasherDryerFolderSystemStrategy
{
    ctor(MachineWashingHandDringHandFoldingSystem first,
       MachineWashingHandDringHandFoldingSystem second,
       HandWashingHandDringHandFoldingWithBreakfastSystem third) { ... }

    public void DoLaundry(int userInput)
    {
       if(userInput == 1)
           first.DoLaundry();
       if(userInput == 2)
           second.DoLaundry();
       if(userInput == 3)
           third.DoLaundry();
    }
}

// MACHINEWASH_HANDDRY_HANDFOLD
public class MachineWashingHandDringHandFoldingSystem : WasherDryerFolderSystem
{
    public MachineWashingHandDringHandFoldingSystem
        (MachineWasher machineWasher, HandDryer handDryer, HandFolder handFolder) :
        base(machineWasher, handDryer, handFolder)
    {
    }
}

// HANDWASH_HANDDRY_HANDNOFOLD
public class HandWashingHandDringHandFoldingSystem : WasherDryerFolderSystem
{
    public MachineWashingHandDringHandFoldingSystem
        (HandWasher machineWasher, HandDryer handDryer, HandFolder handFolder) :
        base(machineWasher, handDryer, handFolder)
    {
    }
}

// HANDWASH_HANDDRY_HANDNOFOLD_WITHBREAKFAST
public class HandWashingHandDringHandFoldingWithBreakfastSystem : WasherDryerFolderSystem
{
   private readonly BreakfastMaker breakfastMaker

   public MachineWashingHandDringHandFoldingSystem
       (HandWasher machineWasher, HandDryer handDryer, HandFolder handFolder, BreakfastMaker brekfastMaker) :
        base(machineWasher, handDryer, handFolder)
    {
        this.breakfastMaker = breakfastMaker
    }

    public overide void DoLaundry()
    {
        base.DoLaundry();
        brekfastMaker.AndMakeChipBreakAsWell();
    }
}

请注意,上面的实现不需要任何Ninject配置。 Ninject会在第一次使用时自动绑定所有ToSelf()(只要它不是接口)。

通常,只要您不需要某种具有多个实现的复合/批量操作,就应该避免接口绑定(以及接口)。像操作示例一样复合:

// original WasherDryerFolderSystem refactored
public class WasherDryerFolderSystem
{
    private IEnumerable<IWasher> washers;
    private IEnumerable<IDryer> dryers;
    private IEnumerable<IFolder> folders;

    public WasherDryerFolderSystem(
        IWasher[] washers, IDryer[] dryers, IFolder[] folders)
    {
        this.washers = washers;
        this.dryers = dryers;
        this.folders = folders;
    }

    // all inclusive
    public virtual void DoLaundry()
    {
        foreach (var washer in washers)
            washer.Wash();

        foreach (var dryer in dryers)
            dryer.Dry();

        foreach (var folder in folders)
            folder.Fold();
    }
}

我希望有所帮助。