Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IoC: Wiring up dependencies on event handlers

I am building an WinForms application with a UI that only consists of a NotifyIcon and its dynamically populated ContextMenuStrip. There is a MainForm to hold the application together, but that is never visible.

I set out to build this as SOLIDly as possible (using Autofac to handle the object graph) and am quite pleased with my success, mostly getting along pretty well even with the O part. With the extension I am currently implementing it seems I have discovered a flaw in my design and need to remodel a bit; I think know the way I need to go but am a bit unclear as to how to exactly define the dependencies.

As mentioned above, the menu is in part populated dynamically after starting the application. For this purpose, I defined an IToolStripPopulator interface:

public interface IToolStripPopulator
{
    System.Windows.Forms.ToolStrip PopulateToolStrip(System.Windows.Forms.ToolStrip toolstrip, EventHandler itemclick);
}

An implementation of this is injected into the MainForm, and the Load() method calls PopulateToolStrip() with the ContextMenuStrip and a handler defined in the form. The populator's dependencies are only related to obtaining the data to use for the menu items.

This abstraction has worked nicely through a few evolutionary steps but isn't sufficient anymore when I need more than one event handler, e.g. because I am creating several different groups of menu items - still hidden behind a single IToolStripPopulator interface because the form shouldn't be concerned with that at all.

As I said, I think I know what the general structure should be like - I renamed the IToolStripPopulator interface to something more specific* and created a new one whose PopulateToolStrip() method does not take an EventHandler parameter, which is instead injected into the object (also allowing for much more flexibility regarding the number of handlers required by an implementation etc.). This way my "foremost" IToolStripPopulator can very easily be an adapter for any number of specific ones.

Now what I am unclear on is the way I should resolve the EventHandler dependencies. I think the handlers should all be defined in the MainForm, because that has all the other dependencies needed to properly react to the menu events, and it also "owns" the menu. That would mean my dependencies for IToolStripPopulator objects eventually injected into the MainForm would need to take dependencies on the MainForm object itself using Lazy<T>.

My first thought was defining an IClickHandlerSource interface:

public interface IClickHandlerSource
{
    EventHandler GetClickHandler();
}

This was implemented by my MainForm, and my specific IToolStripPopulator implementation took a dependency on Lazy<IClickHandlerSource>. While this works, it is inflexible. I would either have to define separate interfaces for a potentially growing number of handlers (severely violating OCP with the MainForm class) or continuously extend IClickHandlerSource (primarily violating ISP). Directly taking dependencies on the event handlers looks like a nice idea on the consumers' side, but individually wiring up the constructors via properties of lazy instance (or the like) seems pretty messy - if possible at all.

My best bet currently seems to be this:

public interface IEventHandlerSource
{
    EventHandler Get(EventHandlerType type);
}

The interface would still be implemented by MainForm and injected as a lazy singleton, and EventHandlerType would be a custom enum with the different types I need. This would still not be very OCP compliant, but reasonably flexible. EventHandlerType would obviously have a change for each new type of event handler, as would the resolution logic in MainForm, in addition to the new event handler itself and the (probably) newly written additional implementation of IToolStripPopulator.

Or.... a separate implementation of IEventHandlerSource that (as the only object) takes a dependency on Lazy<MainForm> and resolves the EventHandlerType options to the specific handlers defined in MainForm?

I'm trying to think of a way of actually getting the event handlers out of MainForm in a feasible way, but can't quite seem to right now.

What is my best option here, providing the loosest coupling and most elegant resolution of the different event handlers?

[*Yes, I probably should have left the name alone to really comply with OCP, but it looked better that way.]

like image 696
TeaDrivenDev Avatar asked Jun 15 '12 23:06

TeaDrivenDev


2 Answers

What is my best option here, providing the loosest coupling and most elegant resolution of the different event handlers?

Common solution are not exist and it depends on the global application architecture.

If you want a loosest coupling, EventAggregator pattern can help you in such case (your IEventHandlerSource similar to that):

  • Pattern Description - http://martinfowler.com/eaaDev/EventAggregator.html
  • Implementation in Prism - http://msdn.microsoft.com/en-us/library/ff921122.aspx

But, global events should be used with great caution - they can smudge architecture, because subscribe to the event will be possible anywhere.

Important thing in DI and IoC: lower dependency should not to know about higher dependency. I think, and as Leon said earlier, will be better to create some interface like ITool<T> and store list of tools in the MainForm. After some action MainForm will invoke certain methods in this tools.

like image 160
Nikita Danilov Avatar answered Oct 15 '22 21:10

Nikita Danilov


Firstly, I think you shouldn't claim your mainform must contain all event handlers. You clearly ran into the fact that there are different handlers with different needs (and probably different dependencies), so why should they all be fitted into the same class? You can probably take some inspiration from the way events are handled in other languages. WPF uses Commands, Java has Listeners. Both are objects, not just a delegate, making them easier to deal with in an IOC scenario. It's fairly easy to simulate something like that. You could abuse the tag on your toolbar items, like this: Binding to commands in WinForms or use lambda expressions inside PopulateToolbar (see Is there anything wrong with using lambda for winforms event?) to associate the toolbar item with the correct command. That's assuming that since PopulateToolbar knows which items need to be created it also know which action/command belongs to each item.

The object representing the action can have their own dependencies injected, independently of the main form or other actions. Toolbar items with their own actions can then be added or removed later without affecting your main form or any of the other actions, each action can independently be tested and refactored.

Bottom line, stop thinking about EventHandlers, start thinking about Actions/Commands as an entity in their own right and it will become easier to come up with a suitable pattern. Make sure you understand the Command Pattern, because that's pretty much what you need here.

like image 32
AVee Avatar answered Oct 15 '22 20:10

AVee