Rhett Link
Rhett Link

Reputation: 21

Implement Interface Segregation Principle for .NET Framework interfaces

I am implementing IServiceLocator(CommonServiceLocator package) into my custom ServiceLocator class.

The interface has the following methods to implement:

public class CustomServiceLocator : IServiceLocator
{
    private IServiceProvider _provider;

    public RatioDissectionServiceLocator(IServiceProvider provider)
    {
        _provider = provider;
    }

    public IEnumerable<object> GetAllInstances(Type serviceType)
    {
        throw new NotImplementedException();
    }

    public IEnumerable<TService> GetAllInstances<TService>()
    {
        throw new NotImplementedException();
    }

    public object GetInstance(Type serviceType)
    {
        throw new NotImplementedException();
    }

    public object GetInstance(Type serviceType, string key)
    {
        throw new NotImplementedException();
    }

    public TService GetInstance<TService>()
    {
        return _provider.GetService<TService>();
    }
 }

I don't want to implement all the methods in my class. How can we achieve ISP for inbuilt C# interfaces?

Any help?

Upvotes: 2

Views: 328

Answers (1)

Scott Hannen
Scott Hannen

Reputation: 29312

The Interface Segregation Principle states:

No client should be forced to depend on methods it does not use.

What this means is that despite many unclear and misleading explanations of the principle, a big interface by itself doesn't violate the principle. Perhaps another class actually does depend on all the members of the interface. So we wouldn't look at an interface like IServiceLocator and try to "fix" it somehow.

The ISP is from the point of view of classes that depend on interfaces. If an interface has 20 members and my class depends on all of them, it's not an ISP violation. (It's likely all sorts of other bad things that have nothing to do with the ISP.) If another class depends on the exact same interface and uses only a few members, that's an ISP violation.

In both examples it's the same interface. The principle isn't about the interface. It's about whether or not a class that depends on the interface uses all of its members.

(Another weird example I see a lot is a big interface and a class that implements the interface but throws NotImplementedException for some members. That's also bad, and it's a Liskov Substitution violation, but it has nothing at all to do with ISP. Implementing an interface is not depending on it.)

One way to avoid ISP violations is to write interfaces from the perspective of classes that depend on them. Whatever your class needs from its dependency, write your interface to describe exactly that. If the concrete inner implementation is a framework class with 100 members, wrap it in your own class:

public interface ISmallSegregatedInterface
{
    void DoJustWhatMyClassNeeds();
}

public class TheImplementation : ISmallSegregatedInterface
{
    private readonly FrameworkClassWithLotsOfMembers _inner;

    public TheImplementation(FrameworkClassWithLotsOfMembers inner)
    {
        _inner = inner;
    }

    public void DoJustWhatMyClassNeeds()
    {
        _inner.CallSomeMethod();
    }
}

Now the class that needs to depend on one method can depend on an interface with just that one method. (Over time I've found that this leads to lots of single-method interfaces. I think that logically leads to depending on functions and delegates but a single-method interface is okay.

That is interface segregation. Your classes don't depend on a big interface they don't need. They depend on one ore more interfaces that describe exactly what they need. This is often accomplished by creating the interface from the perspective of the class that needs to depend on it.


What you might find is that no class needs to depend on all of the methods of IServiceLocator.

Maybe all you need is this:

TService GetInstance<TService>();

But does a class really need to depend on a method that can return anything? In other words, you're probably only going to request one or two dependencies. So maybe what you really need is this:

public interface ISomethingSpecificFactory
{
    ISomethingSpecific CreateInstance();
}

Or you might find that you don't need the factory at all - perhaps you can just inject ISomethingSpecific instead of a factory that creates an instance of ISomethingSpecific.

Another way of looking at it: If you don't need to implement all of the methods of IServiceLocator then you don't need to create a class that implements IServiceLocator.


IServiceLocator is a framework interface. Unlike most software we create, it doesn't exist to meet a narrow, specific need. It meets a broader variety of needs that are determined as we write our software. That's why it makes more sense that it has all sorts of varied methods we might not need.

One reason for the ISP is that if lots of classes depend on different members of an interface, we might be pressured to change the interface because of the needs of one client, and those changes affect other clients that depend on other methods, in effect coupling them all together.

We can't change IServiceLocator so that pressure doesn't exist. So technically, even if we did violate the ISP by depending on that interface, it wouldn't have the harmful effect that the ISP protects us from.

Upvotes: 4

Related Questions