4
\$\begingroup\$

My factory is using method injection because I thought this was the best way to make it so far. Besides, I doubt it is a good thing after having to call on its Create method from within a dependent object.

The only way I might think of whilst continuing to use the parameterized factory Create method, is to inject the dependencies directly in the MainPresenter so that it may provide with the dependencies to the method, and I dislike it. It dislike it because it is not the MainPresenter that depends on the ICustomerManagementView and the ICustomerDetailPresenterFactory, it's its dependency. So I would feel like I'm sabotaging my own code by doing so.

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"))
            )
        );
    }
}

So I wonder, is it best to implement the ICustomerManagementPresenterFactory and bind the implementer with it within my CompositionRoot, so that I could provide those dependencies through constructor injection to the Create method which shall no longer take any arguments, or shall I make it otherwise?

What I like of writing a simple interface is that Ninject does it all for me to a factory, and no code is necessary to build an instance of the desired type. Besides, when the constructor of the class to be created uses constructor injection, it seems like it is impossible to have a simple factory interface bound as a factory, and one need to implement the factory interface by hand.

What did I get right/wrong?

\$\endgroup\$
1
\$\begingroup\$

Finally, no need for parameterized Factory.Create method as per this answer:

Properly using parameterized Factory.Create() method using DI.

In short, because Ninject knows how to resolve your returned type, and also your parameterized types, Ninject shall inject whatever needed to instantiate your class.

public class CustomerManagementPresenter : Presenter<ICustomerManagementView> {
    public CustomerManagementPrenseter(ICustomerManagementView view
        , ICustomerDetailPrensterFactory factory)
        : base(view) {
        customerDetailPresenterFactory = factory;
    }

    public void ShowDetailsFor(Customer customer) {
        var detailPresenter = customerDetailPresenterFactory.Create();
        detailPresenter.ShowDetailsFor(customer);
    }

    private readonly ICustomerDetailPresenterFactory customerDetailPresenterFactory;
}

And because both ICustomerManagementView and ICustomerDetailPresenterFactory are known to Ninject, a simple ICustomerDetailPresenterFactory looks like this:

public interface ICustomerDetailPresenterFactory { 
    CustomerDetailPresenterFactory Create();
}

And when the Create method is called, Ninject will know it has to instantiate everything necessary to have a consistent instance of CustomerDetailPresenter, so it will:

  1. Instantiate an ICustomerDetailView to whatever it is bound in your CompositionRoot
  2. Instantiate an ICustomerDetailPresenterFactory using its Factory Extension
  3. Then finally instantiate your CustomerDetailPresenter class so that you may work with just like if you would have instantiated it yourself.

Besides, the ICustomerDetailPresenterFactory need to be bound using either two methods:

  1. Using convention binding
var kernel = new StandardKernel();
kernel.Bind(services => services
    From(GetProjectAssemblies())
    .SelectAllInterfaces()
    .EndingWith("Factory")
    .BindToFactory());
  1. Using per-factory binding
var kernel = new StandardKernel();
kernel.Bind<ICustomerManagementPresenterFactory>().ToFactory();
kernel.Bind<ICustomerDetailPresenterFactory>().ToFactory();
...

It's as simple as that!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.