3

I would like to ask someone who can help in introduce dependency injection into sample WinForm app. We have an application with following structure (not complete graph):

MainForm <-> MainModel
| |└ -----------------------┐
| └ ------------┐           |
FA <-> MA    FB <-> MB    FC <-> MC
| |                       |└-----------------┐       
| └-----------┐           └----┐             |
FAA <-> MAA   FAB <-> MAB      FCA <-> MCA  FCB <-> MCB
                               |||
                ┌--------------┘|└----------------┐
               FCAA <-> MCAA   FCAB <-> MCAB   FCAC <-> MCAC
               ||
┌--------------┘└----------------┐
FCAAA <-> MCAAA                 FAB <-> MAB

I can implement composition root where I can type something like this:

var container = new Container();
container.RegisterSingle<IMainForm, MainForm>();
container.Register<IMainModel, MainModel>();
container.Register<IFA, FA>();

...

I know how to get the first MainForm and show it (simple resovling). But I am still struggling with proper logic how to create other forms and models on demand. I do not want to pass reference to my container into mainform and resolve dependencies in mainform because it smells as servicelocator pattern. I would like to avoid to create obscure Factory with methods as CreateFA, CreateFB, CreateFAAA... I would like to avoid to pass complete constructed graph into mainform too.

Furthermore I would like to use in MCAA some implementation of an interface IJob which can be defined in composition root, but I would like to avoid to pass instance of IJob thru all levels of forms or passing some kind of factory...

The easiest way how to create all instances of windows and models and ect. as I need them is using ServiceLocator... But, well... What about DI?

Preferred DI: SimpleInjector, Prefered language: C#

user2126375
  • 1,594
  • 12
  • 29

2 Answers2

2

I think you basically have 3 options don't you? Although you have already decided that you don't really like 2 of them :)

  • Inject the instances of the forms into MainForm from the composition root
  • Inject a single factory for creating all the forms into MainForm in the composition root
  • Inject many factories for creating each Form individually into the MainForm in the composition root.

I prefer the third option to the second as each form only needs the factories for the forms it actually needs to create.

How viable the first option is might depend on whether or not your forms are single instance or not.

DI and a container are not magically going to provide a solution for you, they will just make wiring up your preferred solution easier.

Sam Holder
  • 32,535
  • 13
  • 101
  • 181
1

I would like to avoid to pass complete constructed graph into mainform too.

It would make your life much easier, if you design your application to be composed entirely up-front.

If your concern is related to performance, start by composing the entire graph up-front and measure to see if you have a performance problem at all. If you discover that you have sub-graphs that you need to defer, you can use the Virtual Proxy pattern to defer creation of branches until you need them.

If the real concern is that you can't create the entire graph up-front because some sub-graphs depend on run-time values, you can use an Abstract Factory; you don't have to come up with a myriad Abstract Factory interfaces: a single generic interface IFactory<T, TInput> should suffice.

Other alternatives for selecting dependencies based on run-time values are

Furthermore I would like to use in MCAA some implementation of an interface IJob

Just inject IJob into MCAA just like any other class. You don't have to pass it through layers if those layers never create MCAA. Only the Composition Root needs to hold a reference to the implementation, because only the Composition Root creates MCAA.

Community
  • 1
  • 1
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Thank you for an answer. I would say that IFactory is very close to ServiceLocator and violates SOLID principles (as you wrote in article http://blog.ploeh.dk/2014/05/15/service-locator-violates-solid/) or am I mistaken? Also If someone will see this interface how he can know which types it must be able to resolve? Is it defined by some kind of constraints for T param? Or something else? What is the best way to prevent exception "unable to create an instance" somewhere deep in my application, if this generic abstract factory is used? – user2126375 Jul 19 '15 at 19:19
  • Such an interface isn't a Service Locator: http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryorServiceLocator – Mark Seemann Jul 19 '15 at 19:34