views:

1237

answers:

8

I have an app which consists of several different assemblies, one of which holds the various interfaces which the classes obey, and by which the classes communicate across assembly boundaries. There are several classes firing events, and several which are interested in these events.

My question is as follows: is it good practice to implement a central EventConsolidator of some kind? This would be highly coupled, as it would need to know every class (or at least interface) throwing an event, and every consumer of an event would need to have a reference to EventConsolidator in order to subscribe.

Currently I have the situation where class A knows class B (but not C), class B knows class C, etc. Then if C fires an event B needs to pick it up and fire its own event in order for A to respond. These kinds of chains can get quite long, and it may be that B is only interested in the event in order to pass it along. I don't want A to know about C though, as that would break encapsulation.

What is good practice in this situation? Centralise the events, or grin and bear it and define events in each intermediate class? Or what are the criteria by which to make the decision? Thanks!

Edit: Here is another question asking essentially the same thing.

+1  A: 

I'd probably try to massage the domain so that each class can directly depend on the appropriate event source. What I mean is asking the question why don't A know about C? Is there perhaps a D waiting to emerge?

As an alternative approach you could consider an event broker architecture. It means observers don't know directly about the source. Here's an interesting video.

Cristian Libardo
+1  A: 

This would be highly coupled, as it would need to know every class

I think you answered your own question if you consider that coupling is bad! Passing events through a chain of potential handlers is a fairly common pattern in many environments; It may not be the most efficient approach, but it avoids the complexity that your suggested approach would involve.

Another approach you could take is to use a message dispatcher. This involves using a common message format (or at least a common message header format) to represent events, and then placing those messages into a queue. A dispatcher then picks up each of those events in turn (or based on some prioritisation), and routes them directly to the required handler. Each handler must be registered with the dispatcher at startup.

A message in this case could simply be a class with a few specific fields at the start. The specific message could simply be a derivative, or you could pass your message-specific data as an 'object' parameter along with the message header.

Damian Powell
+7  A: 

You could put the event itself in an interface, so that A didn't need to know about C directly, but only that it has the relevant event. However, perhaps you mean that the instance of A doesn't have sight of an instance of C...

I would try to steer clear of a centralised event system. It's likely to make testing harder, and introduced tight coupling as you said.

One pattern which is worth knowing about is making event proxying simple. If B only exposes an event to proxy it to C, you can do:

public event FooHandler Foo
{
    add
    {
        c.Foo += value;
    }
    remove
    {
        c.Foo -= value;
    }
}

That way it's proxying the subscription/unsubscription rather than the act of raising the event. This has an impact on GC eligibility, of course - which may be beneficial or not, depending on the situation. Worth thinking about though.

Jon Skeet
Thanks Jon, instantly helpful as always! That was what I was looking for in this case, cheers.
Joel in Gö
A word of caution. It can be very confusing when the value "sender" is not the object that you subscribed to.
Andreas
+1  A: 

This thread seems similar to your question...
http://stackoverflow.com/questions/217233/bubbling-up-events

Gishu
+1  A: 

What you could try is using the event brokering of either NInject or the Unity Application Block.

This allows you to, for example:

[Publish("foo://happened")]
public event EventHandler<FooArgs> FooHappened;

[Subscribe("foo://happened")]
public void Foo_Happened(object sender, FooArgs args)
{ }

If both objects are created through the container the events will be hooked up automatically.

Jonathan C Dickinson
+1  A: 

You can check out the EventBroker object in the M$ patterns and practises lib if you want centralised events.

Personally I think its better to think about your architecture instead and even though we use the EventBroker here, none of our new code uses it and we're hoping to phase it out one sunny day.

Quibblesome
A: 

is the

[Publish("foo://happened")] public event EventHandler FooHappened;

[Subscribe("foo://happened")] public void Foo_Happened(object sender, FooArgs args) { }

thing, a Ninject feature? or do I need some other library to complement ninject to achive this? OR is it a Unity feature?

/Andy

A: 

we have our own event broker implementation (open source) Tutorial at: http://sourceforge.net/apps/mediawiki/bbvcommon/index.php?title=Event%5FBroker

And a performance analysis at: www.planetgeek.ch/2009/07/12/event-broker-performance/

Advantages compared to CAB: - better logging - extension support - better error handling - extendable handlers (UI, Background Thread, ...) and some more I cannot recall right now.

Cheers, Urs

Urs Enzler