1

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

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Guy Levin
  • 1,230
  • 1
  • 10
  • 22
  • 1
    Can you provide some sample code and explain what you are trying to achieve? – dotnetom Jun 15 '15 at 06:32
  • 3
    You can inject [Concrete Dependencies](http://blog.ploeh.dk/2012/08/31/ConcreteDependencies) into clients, but whether or not it's going to help you depends on your motivation for doing it in the first place. Why do you want to do it? – Mark Seemann Jun 15 '15 at 06:35
  • Edited the post with an example. The reason for doing this is for testing, I dont want the inner class to affect. Also, I want a way of replacing the inner class with something else with injection – Guy Levin Jun 15 '15 at 06:42
  • 2
    You could create a wrapper for TcpServer which implements IServer – IronSlug Jun 15 '15 at 08:09
  • 1
    This was my intention, but if the class does not have default constructor it gets difficult – Guy Levin Jun 15 '15 at 08:40
  • @GuyLevin How does not having a default constructor make making a an [Adapter](https://en.wikipedia.org/wiki/Adapter_pattern) difficult? – Mark Seemann Jun 15 '15 at 11:09
  • @Mark Seemann, It is not a problem that cant be solved, but in that case you should pass the adapter the constructor parameters so it could initiate the original class, so you probably will have to pass the parameters in the dependency configuration – Guy Levin Jun 15 '15 at 14:09
  • @GuyLevin Why not just pass the concrete class object into the Adapter's constructor? – Mark Seemann Jun 15 '15 at 14:15
  • 1
    @Mark Seemann its a nice solution, thanks. Isn't it a bit complex? Maybe there is a simpler solution? Alot of overhead for DI – Guy Levin Jun 15 '15 at 14:25
  • @Mark Seemann also, if you can post your answer (not as comments). I want to mark it as answer – Guy Levin Jun 15 '15 at 14:38

1 Answers1

10

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.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Mark, did you know that instead of linking at APPP, you can also simply refer to the Wikipedia article of the DIP, because it states: "the abstracts are owned by the upper/policy layers". Saves the OP from buying that book :-). But course APPP is a must-read for every developer of course, so pointing at it is no crime :D – Steven Jun 15 '15 at 17:36
  • That's because this is a fairly recent change to the article. The change was made in [January 2015](https://en.wikipedia.org/w/index.php?title=Dependency_inversion_principle&diff=666499143&oldid=643053252). But it's great change; I quote this part quite regularly now. Saves me from referencing APPP. – Steven Jun 15 '15 at 19:11
  • 1
    @Steven Until it goes away again :) – Mark Seemann Jun 15 '15 at 19:28
  • That's the beauty of Wikipedia :-D – Steven Jun 15 '15 at 19:43
  • @Mark Seemann Thank you very much ! Can you please add the Ninject initiation of the TcpServer? Lets say it gets ip in the constructor, just want to make sure I get the full picture – Guy Levin Jun 15 '15 at 19:59
  • 1
    @GuyLevin I have no idea how Ninject works. Do yourself a favour and use [Pure DI](http://blog.ploeh.dk/2014/06/10/pure-di) instead of a DI Container. – Mark Seemann Jun 15 '15 at 20:07
  • @GuyLevin what kind of object do you want to wrap into this adapter ? There is no TcpServer class in the [System.Net.Sockets](https://msdn.microsoft.com/en-us/library/System.Net.Sockets) namespace, but TcpListener (for listening) and TcpClient (for connecting to bound listener). – kayess Jun 16 '15 at 09:07
  • @kayess It was an abstract question, indeed System.Net.Sockets has TcpListener and TcpClient classes – Guy Levin Jun 16 '15 at 12:51