Nikhil Vartak
Nikhil Vartak

Reputation: 5117

Interface Segregation Principle - How to decide what to segregate?

I believe the question is self explanatory. I would rather put more focus on the example to support question.

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

public interface ICollection : IEnumerable
{
    void CopyTo(Array array, int index);
    int Count { get; }
    Object SyncRoot { get; }
    bool IsSynchronized { get; }
}

public interface IList : ICollection
{
    Object this[int index] { get; set; }
    int Add(Object value);
    bool Contains(Object value);
    void Clear();
    bool IsReadOnly { get; }
    bool IsFixedSize { get; }
    int IndexOf(Object value);
    void Insert(int index, Object value);
    void Remove(Object value);
    void RemoveAt(int index);
}

It is clear that IEnumerable is separate to allow only looping over collections. But I don't understand why they kept ICollection and IList separate? Even if they were one, IList would not become fat because that behavior would always be required by any collection?

I came across this post which says your API would return IEnumerable if client only needed to loop of items, ICollection if only read-only access was required and so on.

How did MSFT apply ISP so perfectly that it has never caused any issues all these years?

Is applying ISP a continued process based on client code needs or a one time application? If ISP is applied only once, then my question is in the post title itself.

Upvotes: 3

Views: 325

Answers (1)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726589

I don't understand why they kept ICollection and IList separate?

IList is a collection where obtaining an item by index makes logical sense. Separation of it into a sub-interface is made in recognition of the fact that other collections exist for which indexing is undefined. ISet is an example of such collection.

Merging ICollection and IList would have a negative consequence on one's ability to define ISet in a clean way because of having to implement these four methods:

this[int index]
int IndexOf(Object value)
void Insert(int index, Object value)
void RemoveAt(int index)

On the other hand, one could reasonably argue that methods

int Add(Object value);
bool Contains(Object value);
void Clear();
bool IsReadOnly { get; }
bool IsFixedSize { get; }
void Remove(Object value);

could belong to ICollection interface.

Interface Segregation Principle - How to decide what to segregate?

The task of deciding what to segregate is much easier in retrospect, when the classes are in place. See if any class is required to throw a not-implemented exception. These methods are prime candidates to be moved into a sub-interface that needs to be introduced.

For example, consider starting a design with IList methods moved into ICollection, and no IList sub-interface. You can successfully build List and LinkedList classes with these two interfaces.

Now consider adding HashSet to the mix. This class must implement four methods that are out of place with a set collection, which suggests that you should consider adding one or two sub-interfaces - one for lists, and another one for sets.

It is much harder to decide on segregation before you have any classes in place, because your interface tends to meet the needs of a particular class that needs to use it. It is not uncommon to start with a flat interface structure, and expand it through refactoring in subsequent releases.

Upvotes: 2

Related Questions