This is really nothing new, but I don't think I've explicitly stated this before: It makes a lot of sense to view delegates as anonymous one-method interfaces.

Many people liken delegates to function pointers. While that's probably correct (I wouldn't really know), it's not a very object-oriented view to take - at least not when we are dealing with managed code. To me, it makes more sense to view delegates as anonymous one-method interfaces.

Lets consider a simple example. As always, we have the ubiquitous MyClass with its DoStuff method. In this example, DoStuff takes as input an abstraction that takes a string as input and returns an integer - let's imagine that this is some kind of Strategy (notice the capital S - I'm talking about the design pattern, here).

In traditional object-oriented design, we could solve this by introducing the IMyInterface type:

public interface IMyInterface
{
    int DoIt(string message);
}

The implementation of DoStuff is simply:

public string DoStuff(IMyInterface strategy)
{
    return strategy.DoIt("Ploeh").ToString();
}

Hardly rocket science…

However, defining a completely new interface just to do this is not really necessary, since we could just as well have implemented DoStuff with a Func<string, int>:

public string DoStuff(Func<string, int> strategy)
{
    return strategy("Ploeh").ToString();
}

This not only frees us from defining a new interface, but also from implementing that interface to use the DoStuff method. Instead, we can simply pass a lambda expression:

string result = sut.DoStuff(s => s.Count());

What's most amazing is that RhinoMocks understands and treats delegates just like other abstract types, so that we can write the following to treat it as a mock:

// Fixture setup
Func<string, int> mock =
    MockRepository.GenerateMock<Func<string, int>>();
mock.Expect(f => f("Ploeh")).Return(42);
var sut = new MyClass();
// Exercise system
string result = sut.DoStuff(mock);
// Verify outcome
mock.VerifyAllExpectations();
// Teardown

Whenever possible, I prefer to model my APIs with delegates instead of one-method interfaces, since it gives me greater flexibility and less infrastructure code.

Obviously, this technique only works as long as you only need to abstract a single method. As soon as your abstraction needs a second method, you will need to introduce a proper interface or, preferably, an abstract base class.


Comments

jonnie savell
"While that's probably correct (I wouldn't really know), it's not a very object-oriented view to take."

We shouldn't believe that delegates are unlike a function pointer just because the latter is not object-oriented. The shoe ... fits. Furthermore, I would argue that an anonymous one-method interfaces is not a first-class object-oriented concept; we can describe it with words, but I doubt that you will find any of the non-.NET literature talking about such a thing. Well ... I will grant that mention might be made under a description of the command pattern.

"Obviously, this technique only works as long as you only need to abstract a single method."

Yes. Then we are in trouble and we didn't even swim that far from shore.

What was the problem? We focussed too much on a method and we ignored the interface. An interface defines the contract of which the method is only a part. The contract is identified by the name of the interface. There is no contract defined by method signatures. "Takes an int and a double and returns a string" doesn't mean anything.

In summary, focussing on the method is every bit as dirty as ... function pointers.

Sincerely,
jonnie savell
2010-04-07 07:47 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or Google Plus, or somewhere else with a permalink. Ping me with the link, and I may add it as a comment.

Published

Thursday, 28 May 2009 20:19:04 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!