Question

To remind (from wiki):

The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.

And now look at my example.

Here's my mutable entity. It is edited from somewhere and it is able to notify about changes through read-only interface:

interface ICounter
{
    event Action<int> NewNumber;
}

class Counter : ICounter
{
    public event Action<int> NewNumber;

    int number = 0;
    public void IncrementAndSend(int x)
    {
        number += x;
        if (NewNumber != null) NewNumber(number);
    }
}

And here's the class from transport layer which uses it. Look at two variants of injection (Attach_1 and Attach_2) and my assumptions below:

class OneToManyRouter
{
    IEnumerable<Counter> _destinations;

    public OneToManyRouter(IEnumerable<Counter> destinations)
    {
        _destinations = destinations;
    }

    // 1         
    public void Attach_1(ICounter source)
    {
        source.NewNumber += (n) =>
        {
            foreach (var dest in _destinations) dest.IncrementAndSend(n);
        };
    }

    // 2 
    public void Attach_2(Counter source)
    {
        source.NewNumber += (n) =>
        {
            foreach (var dest in _destinations) dest.IncrementAndSend(n);
        };
    }
}
  1. ISP is about real objects. You must not use "excessive" references to incoming parameters.
  2. ISP is about classes. If your class already uses full interface somewhere, no need to restrict reference type in particular methods. ICounter interface is excessive in this example.
  3. This architecture is completely wrong from the point of SOLID principles (then why?).
Was it helpful?

Solution

ISP is neither about classes, nor objects, but strictly about interfaces, in particular about interface design. The principle is intended to discourage the grouping of semi-related methods in a single interface to avoid users only needing a sub-set of these methods to implement the rest as empty functions (either throwing a NotImplementedException or leaving it literally empty: { }). Thus it easier to implement more coherent classes and use the interfaces more effectively.

In your example you are combining both the Counter class with the ICounter interface, in a way that does not directly relates to the ISP concept:

  1. ISP is about real objects. You must not use "excessive" references to incoming parameters.

This is partly true (if I interpret the concept of "excessive" correctly). However, like I mentioned ISP is not about how to interact with real objects but how to define an useful interface.

  1. ISP is about classes. If your class already uses full interface somewhere, no need to restrict reference type in particular methods. ICounter interface is excessive in this example.

This is not correct. The fact the class implements the interface does not mean anything if you create a dependency on the concrete class rather than on the interface. Remember the interface provides a decoupling of the various parts of you program by making the components rely on contracts rather than specific implementation that might change in the future. By using the concrete class you lose this benefit. And again, this is not entirely related to the ISP concept.

  1. This architecture is completely wrong from the point of SOLID principles (then why?).

From an architectural point of view with an emphasis on SOLID principles, I would suggest having the dependency on ICounter rather than Counter, and including IncrementAndSend as part of the interface definition.

OTHER TIPS

I think that it would be better to add the IncrementAndSend method to the interface and make the class OneToManyRouter depend on IEnumerable<ICounter>. In my opinion this is not an ISP violation because the NewNumber event action and the IncrementAndSend method are strictly related. Hope it can help you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top