使用DI正确使用参数化的Factory.Create()方法

时间:2014-09-10 16:34:33

标签: c# dependency-injection ninject mvp factory-pattern

我的工厂正在使用方法注入,因为我认为这是迄今为止最好的方法。此外,我怀疑在必须从依赖对象中调用其Create方法后,这是一件好事。

我可能想到的同时继续使用参数化工厂的唯一方法Create的方法,是直接在注入的依赖关系的MainPresenter,以便它可以与依赖关系的方法提供,并我不喜欢它。它不喜欢它,因为MainPresenter不依赖于ICustomerManagementViewICustomerDetailPresenterFactory,它依赖于它。所以我觉得我这样做是在破坏自己的代码。

MainPresenter

public class MainPresenter : Presenter<IMainView>, IMainViewUiHandler {
    public MainPresenter(IMainView view
        , ICustomerManagementPresenterFactory customerManagementFactory) 
        : base(view) {
        this.customerManagementPresenterFactory = customerManagementPresenterFactory;
    }

    public void ManageCustomers() {
        // The following line is causing trouble.
        // As you can see per the ICustomerManagementPresenterFactory code sample,
        // the Create() method takes two parameters: 
        //   1. ICustomerManagementView, and
        //   2. ICustomerDetailPresenterFactory 
        // Hence I have to provide the dependencies manually, I guess. Which is
        // something to avoid at any cost.            
        var customerManagementPresenter = customerManagementPresenterFactory.Create();
        customerManagementPresenter.ShowView();
    }
}

ICustomerManagementPresenterFactory

public interface ICustomerManagementPresenterFactory {
    // Here. Though I ask Ninject to inject my dependencies, I need to
    // provide values to the parameters when calling the method from within
    // the MainPresenter class. The compiler won't let me do otherwise! And
    // this makes sense!...
    [Inject]
    CustomerManagementPresenter Create(ICustomerManagementView view
                                     , ICustomerDetailPresenterFactory factory);
}

IMainView

public interface IMainView : IView, IHasUiHandler<IMainViewUiHandler> {
}

IMainViewUiHandler

public interface IMainViewUiHandler : IUiHandler {
    void ManageCustomers();
}

IUiHandler

public interface IUiHandler { 
}

IHasUiHandler

public interface IHasUiHandler<H> where H : IUiHandler {
    H Handler { set; }
}

的MainForm

public partial class MainForm : Form, IMainView {
    public MainForm() { InitializeComponent(); }

    public IMainViewUiHandler Handler { private get { return handler; } set { setHandler(value); } }
}

CompositionRoot

public class CompositionRoot {
    private CompositionRoot() { }

    public static IKernel BuildObjectGraph() {
        IKernel kernel = new StandardKernel();
        BindFactories(kernel);
        BindViews(kernel);
    }

    private static void BindFactories(IKernel kernel) {
        kernel.Bind(services => services
            .From(AppDomain.CurrentDomain
                .GetAssemblies()
                .Where(a => !a.FullName.Contains("Tests")))
            .SelectAllInterfaces()
            .EndingWith("Factory")
            .BindToFactory()
        );
    }

    private static void BindViews(IKernel kernel) {
        kernel.Bind(services => services
            .From(AppDomain.CurrentDomain
                .GetAssemblies()
                .Where(a => a.FullName.Contains("Windows")
                         && !a.FullName.Contains("Tests"))
            .SelectAllClasses()
            .EndingWith("Form")
            .BindSelection((type, baseType) => type
                .GetInterfaces()
                .Where(iface => iface.Name.EndsWith("View"))
            )
        );
    }
}

所以我想,是它最好地实现内与它的ICustomerManagementPresenterFactory,并结合实施者我的CompositionRoot,这样我可以提供通过构造器注入这些依赖于Create方法不再提出任何论据,或者我不应该提出任何论据?

我喜欢编写一个简单的界面是Ninject为我工厂所做的一切,并且不需要代码来构建所需类型的实例。此外,当要创建的类的构造函数使用构造函数注入时,似乎不可能将简单的工厂接口绑定为工厂,并且需要手动实现工厂接口。

我得对错了什么?

1 个答案:

答案 0 :(得分:1)

事实上,您根本不需要将参数传递给工厂Create方法 - 除非它们是需要“向下”传递的参数,因为它们不能在组合中绑定root(例如输入值)。但是,将这些参数传递给构造函数通常是代码气味。大多数情况下,最好将这些参数传递给方法而不是构造函数(例如:Adder.Add(5,3);,而不是new Adder(5, 3).ComputeResult();

现在考虑以下示例,该示例完美无缺:

public class Dependency1 { }

public interface IDependency2 { }
public class Dependency2 : IDependency2 { }

public interface IBar { }

public class Bar : IBar
{
    public Bar(Dependency1 d1, IDependency2 d2) { }
}

public interface IBarFactory 
{
    IBar Create();   
}


var kernel = new StandardKernel();
kernel.Bind<IBarFactory>().ToFactory();
kernel.Bind<IBar>().To<Bar>();
kernel.Bind<Dependency1>().ToSelf();
kernel.Bind<IDependency2>().To<Dependency2>();

var factory = kernel.Get<IBarFactory>();
var bar = factory.Create();

bar.Should().BeOfType<Bar>();

即使Bar有两个构造函数参数,生成的IBarFactory的{​​{1}}方法也没有指定。没问题,ninject会自动解决它。

现在让我举个例子Create()实际产生的结果。考虑工厂:

.ToFactory()

会导致(注释:它是通过拦截器实现的,而不是通过编织实现的,所以示例是简化):

public interface ISomeFactory
{
    ISomething Create(string parameter1);
}

public class SomeFactory : ISomeFactory { private readonly IResolutionRoot resolutionRoot; public SomeFactory(IResolutionRoot resolutionRoot) { this.resolutionRoot = resolutionRoot; } public ISomething Create(string parameter1) { this.resolutionRoot.Get<ISomething>(new ConstructorArgument("parameter1", parameter1); } } 告诉ninject将ConstructorArgument的值传递给名为“parameter”的ctor参数。

所有其他参数“照常”解决。如果无法解析构造函数参数(既不作为参数传递也不作为参数传递),则ninject将抛出一个异常,指出该参数无法解析。