2

Does it make sense to have interfaces like IAddable, IRemovable, IIndexable, to support each operation or would this sort of approach ultimately lead to unmanageable code?

user420667
  • 6,552
  • 15
  • 51
  • 83
  • unless you uncounted a problem with putting them in the same interface don't split them.. – ro-E Aug 03 '15 at 18:35
  • @ro-E I'm just thinking a lot of times I don't necessarily want to implement some of the other methods that often come with a class if i'm only going to use say the Add method. I'm thinking specifically of inheriting from a list. – user420667 Aug 03 '15 at 18:37
  • normally a class that has a "add" method will have a "remove" method.. unless you're dealing with special cases.. moreover you don't have to implement some methods there's nothing wrong with leaving a black implementation (with the correct comments) – ro-E Aug 03 '15 at 18:39
  • The question is way to short to give anything of a good answer because if this is sensible or not is highly depending on the circumstances. Is this more like a CRUD operation then keep them together may seem like a good idea. But for containers where some of them might be readonly it is better to split. – Philip Stuyck Aug 03 '15 at 18:51
  • @Philip Stuyck: If they need to be together why not just have a new interface that has both? LIke IAddandRemoveable : IAddable, IRemovable. – user420667 Aug 03 '15 at 19:00
  • @user420667 if you don't need this very small granularity why bother. There is also a thing called Yagni. You ain't gonna need it. It simply might be overdesign in some situations where the 2 things never seem to appear seperated. – Philip Stuyck Aug 03 '15 at 19:02
  • 1
    @Philip Stuyck: Actually yagni is the very thing I had in mind when I wanted to add just an IAddable or just and IRemoveable, because I figured the remove operation was one I wasn't gonna need :-). I feel like adding the granularity by default makes it easier to group the ones you might want together but that could just be me. – user420667 Aug 03 '15 at 19:07
  • 1
    @user420667 If you are making some kind of container, that might make sense. I am not saying that you should not do this. I cannot do that with the little information of your question. The decision is yours. There is no one solution that fits all in programming. – Philip Stuyck Aug 03 '15 at 19:11

3 Answers3

5

Imagine you want to implement a sort-of NumberBucket that is like a collection for numbers.

You could create a somewhat fat interface like this one:

public interface INumberBucket
{
    void Add(int number);
    void Remove(int number);
}

And you could implement it with no problems:

 public class NumberBucket : INumberBucket
{
    public void Add(int number)
    {
        Console.WriteLine($"Adding {number}");
    }

    public void Remove(int number)
    {
        Console.WriteLine($"Removing {number}");
    }
}

But then later on you decide you need to implement a ReadonlyNumberBucket where numbers cannot be removed:

public class ReadonlyNumberBucket : INumberBucket
{
    public void Add(int number)
    {
        Console.WriteLine($"Adding {number}");
    }

    public void Remove(int number)
    {
        throw new NotImplementedException();
    }
}

Well, now there is no logical implementation for Remove so you have have to make it a no-op or make it throw. This violates the Liskov Substitution Principle.

Had you decalred two focused interfaces: IAddable and IRemovable you could have just not implemented IRemovable. This is the basis for the Interface Segregation Principle.

So, to answer your question: yes - it is reasonable but it may be a while before you see a return on your investment however. And just like @PhilipStuyck wrote in the comments:

[..] The question is if this example is applicable in your situation. The very first interface in this answer might be good enough for you and splitting it might be overdesign. Overdesign is also a code smell. You gotta know when to apply which pattern. Based on the short explanation in the question I cannot tell.

Alex Booker
  • 10,487
  • 1
  • 24
  • 34
  • If your bucket is write-only as in the example, it's actually even more useful to just omit all interfaces altogether ;-) – blubb Aug 03 '15 at 18:44
  • All of those lines with `Console.WriteLine($"...");` don't compile. – Jashaszun Aug 03 '15 at 18:47
  • @Jashaszun They do in Visual Studio 2015. Those are _interpolated strings_. – Alex Booker Aug 03 '15 at 18:48
  • @Petrichor Oh, so it's a C# 6 thing? Do you have a link to this specific feature? – Jashaszun Aug 03 '15 at 18:49
  • 1
    @Jashaszun I actually wrote a small answer on the topic found here: https://stackoverflow.com/questions/31740340/what-is-symbols-in-c-sharp-code/31740363#31740363 – Alex Booker Aug 03 '15 at 18:51
  • This is a really good example. I think the null op is a form of a silent failure, and having it just fail at runtime doesn't seem like a particularly nice option either. – user420667 Aug 03 '15 at 19:01
  • @user420667 I think throwing a an exception is better than failing silently because it is important to fail fast so that the problem does not seem like a bug. I am of the opinion that when possible, we should make things statically-typed pre-conditions whenever possible and in this case, the ISP helps us do that. That being said, throwing a `NotImplementedException` is not _that_ bad just like throwing an `ArgumentNullException` in a guard clause isn't _that_ bad. You need to consider whether interface segregation is something you'll likely benefit from in your own application. – Alex Booker Aug 03 '15 at 19:04
  • This is indeed a good example, but the question is if this example is applicable in your situation. The very first interface in this answer might be good enough for you and splitting it might be overdesign. Overdesign is also a code smell. You gotta know when to apply which pattern. Based on the short explanation in the question I cannot tell. – Philip Stuyck Aug 03 '15 at 19:07
  • @PhilipStuyck Can I add your comment to my answer please? I think it is a useful addition. – Alex Booker Aug 03 '15 at 19:56
  • sure you can add my comment , no problem. – Philip Stuyck Aug 03 '15 at 20:02
  • @PhilipStuyck Done. Thank you. – Alex Booker Aug 03 '15 at 20:08
0

It depends on level of a "control" you have in your application design. If you have items that may be "Addable" but not "Devidable", what you say makes pretty much sense.

But if in your app domain, all items usually support basic operations you describe, don't see a reason to move them into the separate interfaces.

Tigran
  • 61,654
  • 8
  • 86
  • 123
0

I saw some comments about the fact that the Liskov substitution might be broken if you let all methods inside an interface. To be honest, even Microsoft violates the Liskov substitution principle sometimes. For example, they made the Array to implement IList even that some methods on this interface are not supported by the Array type (for example if you cast an array to IList, the "Add" method throws an exception of type NotSupportedException).

The MOST important thing is this: Do you see any logical relation between your methods ? at least they must belong to a certain category or something. if so, group them together into an interface. In your case, IAddable and IRemovable seem for me that are somehow corelated because they both represent something simmilar: an object can be MOVED (added or removed) from some basket. The IIndexable for me seems like a "bonus" like something that you can add but is not mandatory. So if I would be your architect I would put IIndexable methods in a separate interface.

Anyway if in a far future an object would be "addable" but will not support "remove" then you could easelly solve this problem by splitting your interface in two and make our old interface to inherit them both : IAddable, IRemovable. I tend to "segregate" interfaces at the right moment, i don't like splitting my interfaces from the very start of my architecture.

So, remember:

  • In the beginning, segregate only what's obvious then segregate when the situation requires a segregation or even you can choose for the sake of readability / framework understanding to NOT segregate (as microsoft did because for them, an array is a kind of List and this should remain like so).

  • Patterns and principles like SOLID are just a very powerfull guideance but rules are made sometimes to be broken. Keep this in mind.

George Lica
  • 1,798
  • 1
  • 12
  • 23