Reputation: 5117
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
Reputation: 726589
I don't understand why they kept
ICollection
andIList
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