Dependency Injection for concrete .Net classes

4.7k views Asked by At

What is the preferred way for injecting/isolating classes that are sealed in dlls and do not implement interfaces?

We use Ninject.

Let's say that we have a class "Server" and we want to inject/isolate the class TcpServer which "Server" uses.

Don't want to be too specific because I want to know the best way, but let's say something like that:

public class Server 
{
    IServer _server;
    public Server(IServer server) 
    { 
        _server = server;
    }

    public void DoSomething()
    {
        _server.DoSomething();     
    }
}

_server should be injected with, let's say, TcpClient or mock in case of testing

1

There are 1 answers

8
Mark Seemann On BEST ANSWER

If TcpServer is sealed and implements no interfaces, but you still want to decouple a client from its particular implementation, you'll have to define an interface that the client can talk to, as well as an Adapter from TcpServer to the new interface.

It can be tempting to extract an interface from the concrete class, but don't do this. It creates a semantic coupling between the interface and the concrete class, and you'll most like end up breaking the Liskov Substitution Principle.

Instead, define the interface in terms of the what the client needs. This follows from the Dependency Inversion Principle; as APPP, chapter 11 explains: "clients [...] own the abstract interfaces". A Role Interface is best.

So if your client needs a DoSomething method, that's all you add to the interface:

public interface IServer
{
    void DoSomething();
}

You can now inject IServer into your client, using Constructor Injection:

public class Client 
{
    private readonly IServer server;

    public Client(IServer server) 
    {
        if (server == null)
            throw new ArgumentNullException("server");

        this.server = server;
    }

    public void DoFoo()
    {
        this.server.DoSomething();     
    }
}

When it comes to TcpServer, you can create an Adapter over it:

public class TcpServerAdapter : IServer
{
    private readonly TcpServer imp;

    public TcpServerAdapter(TcpServer imp)
    {
        if (imp == null)
            throw new ArgumentNullException("imp");

        this.imp = imp;
    }

    public void DoSomething()
    {
        this.imp.DoWhatever();
    }
}

Notice that the methods don't have to have the same names (or even exact same signatures) in order to be adapted.