GETah
GETah

Reputation: 21449

Delegates (Lambda expressions) Vs Interfaces and abstract classes

I have been looking for a neat answer to this design question with no success. I could not find help neither in the ".NET Framework design guidelines" nor in the "C# programing guidelines". I basically have to expose a pattern as an API so the users can define and integrate their algorithms into my framework like this:

1)

// This what I provide
public abstract class AbstractDoSomething{
   public abstract SomeThing DoSomething();
}

Users need to implementing this abstract class, they have to implement the DoSomething method (that I can call from within my framework and use it)

2)

I found out that this can also acheived by using delegates:

public sealed class DoSomething{
   public String Id;
   Func<SomeThing> DoSomething;
}

In this case, a user can only use DoSomething class this way:

DoSomething do = new DoSomething()
{
  Id="ThisIsMyID",
  DoSomething = (() => new Something())
}

Question

Which of these two options is best for an easy, usable and most importantly understandable to expose as an API?

EDIT

In case of 1 : The registration is done this way (assuming MyDoSomething extends AbstractDoSomething:

MyFramework.AddDoSomething("DoSomethingIdentifier", new MyDoSomething());

In case of 2 : The registration is done like this:

MyFramework.AddDoSomething(new DoSomething());

Upvotes: 19

Views: 3365

Answers (5)

Anand
Anand

Reputation: 14955

Though personally, if in my hand, I would always go by Delegate Model. I just love the simplicity and elegance of higher order functions. But while implementing the model, be careful about memory leaks. Subscribed events are one of the most common reasons of memory leaks in .Net. This means, suppose if you have an object that has some events exposed, the original object would never be disposed until all events are unsubscribed since event creates a strong reference.

Upvotes: 2

Jason Coyne
Jason Coyne

Reputation: 6636

A few things to consider :

How many different functions/delegates would need to be over-ridden? If may functions, inheretance will group "sets" of overrides in an easier to understand way. If you have a single "registration" function, but many sub-portions can be delegated out to the implementor, this is a classic case of the "Template" pattern, which makes the most sense to be inherited.

How many different implementations of the same function will be needed? If just one, then inheretance is good, but if you have many implementations a delegate might save overhead.

If there are multiple implementations, will the program need to switch between them? Or will it only use a single implementation. If switching is required, delegates might be easier, but I would caution this, especially depending on the answer to #1. See the Strategy Pattern.

If the override needs access to any protected members, then inheretance. If it can rely only on publics, then delegate.

Other choices would be events, and extension methods as well.

Upvotes: 1

David Hoerster
David Hoerster

Reputation: 28701

As is typical for most of these types of questions, I would say "it depends". :)

But I think the reason for using the abstract class versus the lambda really comes down to behavior. Usually, I think of the lambda being used as a callback type of functionality -- where you'd like something custom happen when something else happens. I do this a lot in my client-side code: - make a service call - get some data back - now invoke my callback to handle that data accordingly

You can do the same with the lambdas -- they are specific and are targeted for very specific situations.

Using the abstract class (or interface) really comes down to where your class' behavior is driven by the environment around it. What's happening, what client am I dealing with, etc.? These larger questions could suggest that you should define a set of behaviors and then allow your developers (or consumers of your API) to create their own sets of behavior based upon their requirements. Granted, you could do the same with lambdas, but I think it would be more complex to develop and also more complex to clearly communicate to your users.

So, I guess my rough rule of thumb is: - use lambdas for specific callback or side-effect customized behaviors; - use abstract classes or interfaces to provide a mechanism for object behavior customization (or at least the majority of the object's primary behavior).

Sorry I can't give you a clear definition, but I hope this helps. Good luck!

Upvotes: 1

Ortiga
Ortiga

Reputation: 8824

I would go with the abstract class / interface if:

  • DoSomething is required
  • DoSomething will normally get really big (so DoSomething's implementation can be splited into several private / protected methods)

I would go with delegates if:

  • DoSomething can be treated as an event (OnDoingSomething)
  • DoSomething is optional (so you default it to a no-op delegate)

Upvotes: 7

Reed Copsey
Reed Copsey

Reputation: 564821

Which of these two options is best for an easy, usable and most importantly understandable to expose as an API?

The first is more "traditional" in terms of OOP, and may be more understandable to many developers. It also can have advantages in terms of allowing the user to manage lifetimes of the objects (ie: you can let the class implement IDisposable and dispose of instances on shutdown, etc), as well as being easy to extend in future versions in a way that doesn't break backwards compatibility, since adding virtual members to the base class won't break the API. Finally, it can be simpler to use if you want to use something like MEF to compose this automatically, which can simplify/remove the process of "registration" from the user's standpoint (as they can just create the subclass, and drop it in a folder, and have it discovered/used automatically).

The second is a more functional approach, and is simpler in many ways. This allows the user to implement your API with far fewer changes to their existing code, as they just need to wrap the necessary calls in a lambda with closures instead of creating a new type.

That being said, if you're going to take the approach of using a delegate, I wouldn't even make the user create a class - just use a method like:

MyFramework.AddOperation("ThisIsMyID", () => DoFoo());

This makes it a little bit more clear, in my opinion, that you're adding an operation to the system directly. It also completely eliminates the need for another type in your public API (DoSomething), which again simplifies the API.

Upvotes: 16

Related Questions