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.