tags:

views:

302

answers:

7

Suppose I have a Monkey class which sometimes needs to acquire an instance of Banana. The way this banana is provided is not of interest to the monkey, but it does initiate the banana acquisition.

Now I have at least three possible ways to wire my monkey to a banana provider. What is the best way to do it?

1. Event

Raise a Monkey.BananaNeeded event. The event handler sets the BananaNeededEventArgs.Banana property.

2. Interface

Invoke IBananaProvider.GetBanana. The IBananaProvider instance is injected in the monkey as a constructor argument or through a property.

3. Delegate

Invoke a delegate of type System.Func<Banana>. The delegate is injected in the monkey as a constructor argument or through a property. This one is tempting because it doesn't require the declaration of any extra interfaces or classes, but apparently it is not a popular choice.

+5  A: 

I don't like the event and delegate options unless there is a mechanism to ensure multiple handlers won't be attached. Option 2 is therefore the winner, IMO.

Michael McCloskey
You can't attach more than one method to a delegate...
Guffa
@Guffa - Eh? All delegates in .NET are MulticastDelegate, and the whole point is that you *can* attach multiple methods to it. How do you think events work??
Greg Beech
Yes, you can combine the delegates, but it's totally useless in this case as only the last one can return the banana.
Guffa
+1  A: 

The best way in the above scenario is to go for Interface. In OOAD terms we can define the above scenario as Monkey class has a Banana.

srikanthv
+1  A: 

I usually use the 2.

IBananaProvider bananaProvider = ProviderFactory.Get<IBananaProvider>();
Gregoire
+1 ...but then doesn't the monkey need to know how to get to the factory to pick up his banana?
Justin Niessner
@Justin Niessner: sure,you can have a factory of Providerfactory based on an configuration file.
Gregoire
+4  A: 

The event model works best if there is several possible banana providers, i.e. the monkey asks all providers in turn for a banana, and the first one that can provide one gets to deliver it.

The other two models work fine for a single banana prover per monkey. The delegate is easier to create a provider for, and the interface is more structured.

Guffa
+1  A: 

There are operations on Monkey that make it require the Banana.

The need for the Monkey to get a Banana is du to these operations. You can surely pass the Banana as a parameter for the operations.

I will remove the dependency of the Money on a IBananaProvider.

class Monkey
{
   void FeedWith(Banana banana) { ... }
}

It will be the reponsibility of the caller to get the banana, not the Monkey.

I'm advocating here not to inject services in entities.

Think Before Coding
The monkey might need a new banana when he finishes the first one. :-)
djna
In this case, pass the IBananaProvider as argument, the Monkey can decide how many bananas he wants.
Think Before Coding
But then again, Martin Fowler considers the Anemic Monkey Model to be an anti-pattern: http://martinfowler.com/bliki/AnemicDomainModel.html
Wim Coenen
I don't see why I would make the model anemic ? It's just that the IBananaProvider doesn't belong to Monkey's state.
Think Before Coding
+1  A: 

Ideally dependency injection is the more generally accepted approach.

Another approach to try is message queues.

In this scenario Monkey could essentially "post" a message to some queue for a delicious banana. This "posting" would trigger some provider who is monitoring the queue, to then fulfill the request - which actual instance of provider responds is irrelevant. Monkey would then wait for a response to the original message and take action when the parcel of interest arrives from the BananaProvider.

Message queues decouple providers and consumers. Granted, there is still some form of contract" in place, but who is on the other end of the request and answer is irrelevant.

Mike J
+1  A: 

I'd agree and say that option 2 is the best solution. Delegates are typically meant to, well delegate something, in other words do something, and events are meant for immediate response to something that happened, usually user-related. The interface simply says, "This is here and you can do it." Because you don't want the banana or the monkey doing anything at first and you don't want the user to be directly interacting with the inner workings of your code, an interface wins it. All you need is to know that the monkey can receive bananas. What he decides to do with those bananas afterwards is up to him, and the banana itself as well as the code surrounding should not concern itself with it.

Eli