In my previous post I hinted about a rule which I apply to the design of interfaces:
An interface should have at most one member.
This is to emphasize, that interfaces often model abilities that are mixed in to the main entity. And you mostly need only one member to model an ability.
To illustrate this, here's an example. Sometimes I regret that System.Collections.Generic.ICollection<T> doesn't consist of such more fine granular little interfaces. It would really make things simpler in some scenarios. For example, consider a method which adds a lot of stuff into some bag:
Now my problem is this. I'd also like to also pass objects that don't implement ICollection into this method. I'd only implement the Add method and I don't want to additionally implement Remove, Count, IsReadOnly etc. If ICollection would inherit from IAllowsAdd, things would have been much more simple:
You might ask: why not simply return stuff in an IEnumerable instead of stuffing it into a bag passed as parameter? Well, one could do that, but this would be a little slower - we need some temporary storage where we'd store the output before we actually put it in the bag outside. In time-critical scenarios you can save a lot of time if you directly put your stuff into the bag instead of first returning it and then putting it into the bag somewhere outside the method.
A more concrete example - imagine some code editor would like to show an IntelliSense drop down list box with 1000 words and you have a GetPossibleCompletionWords() method that is supposed to put a 1000 strings into the listbox. If you first return the strings, you have to accumulate them in some temporary list first:
This costs both time and memory. Even if you use iterators and yield return, you will have additional costs connected with storing the method's control flow state machine in the heap. It saves you a lot of time if you directly insert the words into your destination listbox and pass the listbox as a parameter. However, you have a problem: your listbox doesn't implement ICollection. But it does implement IAllowsAdd (or IFillable, or ICanAddStuff or whatever).
That's why I wish the ICollection interface of the .NET Framework would actually consist of more fine granular interfaces which could be reused in places where I don't need the entire collection. And - who knows - maybe such interfaces will be introduced sometime in the future, because splitting an interface into several new interfaces is not a breaking change - one could continue to use existing ICollection, but one also would get new, reusable interfaces like IAllowsAdd, ICountable, ISet, etc.
Please let me know what do you think about it - I welcome your opinions, ideas and suggestions.
Update: it seems that there is a nice workaround with delegates. You can just pass a delegate as parameter to the method. This delegate can point to any Add method you like. This would be even more flexible than using fine-granular interfaces. Hmm... this sounds good: everywhere where you'd use an interface with one method, you could actually use a delegate instead. The performance tradeoff would be virtual call vs. delegate invocation.
An interface should have at most one member.
This is to emphasize, that interfaces often model abilities that are mixed in to the main entity. And you mostly need only one member to model an ability.
To illustrate this, here's an example. Sometimes I regret that System.Collections.Generic.ICollection<T> doesn't consist of such more fine granular little interfaces. It would really make things simpler in some scenarios. For example, consider a method which adds a lot of stuff into some bag:
void AddLotsOfStuff(ICollection bag)
{
bag.Add(stuff1); ... bag.Add(stuff1000000);
}
Now my problem is this. I'd also like to also pass objects that don't implement ICollection into this method. I'd only implement the Add method and I don't want to additionally implement Remove, Count, IsReadOnly etc. If ICollection would inherit from IAllowsAdd, things would have been much more simple:
void AddLotsOfStuff(IAllowsAdd bag)I'm using only the Add ability of the bag inside the method anyway, so I don't care about other ICollection abilities - I don't need them here. And now - voila - I can pass ICollections and my custom objects that solely implement IAllowsAdd.
{
bag.Add(stuff1); ... bag.Add(stuff1000000);
}
You might ask: why not simply return stuff in an IEnumerable instead of stuffing it into a bag passed as parameter? Well, one could do that, but this would be a little slower - we need some temporary storage where we'd store the output before we actually put it in the bag outside. In time-critical scenarios you can save a lot of time if you directly put your stuff into the bag instead of first returning it and then putting it into the bag somewhere outside the method.
A more concrete example - imagine some code editor would like to show an IntelliSense drop down list box with 1000 words and you have a GetPossibleCompletionWords() method that is supposed to put a 1000 strings into the listbox. If you first return the strings, you have to accumulate them in some temporary list first:
StringList results = new StringList();
results.Add(word1); ...
return results;
This costs both time and memory. Even if you use iterators and yield return, you will have additional costs connected with storing the method's control flow state machine in the heap. It saves you a lot of time if you directly insert the words into your destination listbox and pass the listbox as a parameter. However, you have a problem: your listbox doesn't implement ICollection. But it does implement IAllowsAdd (or IFillable, or ICanAddStuff or whatever).
That's why I wish the ICollection interface of the .NET Framework would actually consist of more fine granular interfaces which could be reused in places where I don't need the entire collection. And - who knows - maybe such interfaces will be introduced sometime in the future, because splitting an interface into several new interfaces is not a breaking change - one could continue to use existing ICollection, but one also would get new, reusable interfaces like IAllowsAdd, ICountable, ISet, etc.
Please let me know what do you think about it - I welcome your opinions, ideas and suggestions.
Update: it seems that there is a nice workaround with delegates. You can just pass a delegate as parameter to the method. This delegate can point to any Add method you like. This would be even more flexible than using fine-granular interfaces. Hmm... this sounds good: everywhere where you'd use an interface with one method, you could actually use a delegate instead. The performance tradeoff would be virtual call vs. delegate invocation.
3 comments:
I've posted this suggestion at connect.microsoft.com - let's see now how sane this idea is...
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=295720
Can you show us how it is done with delegate?
Danke!
Bitte schön: http://kirillosenkov.blogspot.com/2007/12/delegates-as-alternative-to-single.html :)
Post a Comment