The Adaptor Pattern is excellent for keeping your code clean even when using messy third-party APIs.
Let's say you're using a third party library (maybe a nuget package) with the interface below:
public IHorriblePrinter
{
// method you want to use
int Print(string text);
// methods you don't care about
int Print(string text, bool reversed);
int Print(string text, bool reversed, string extraText);
// lots of extra methods you don't need...
...
}
And you have some code using this interface:
IHorriblePrinter printer = ...
printer.Print("Some words I want to print!");
The problem here is that your code now depends on the IHorriblePrinter
interface. This is annoying for a few reasons:
IHorribleInterface
interface is difficult to work with. If anyone wanted to implement the interface they have to write code for all of the useless methods, not just the method we actually use.IHorriblePrinter
interface.IHorriblePrinter
and wouldn't know which methods we need to mock.IHorriblePrinter
and it's hard to keep track of which parts of the interface we actually care about.IHorriblePrinter.Print(string text)
method that we want to use returns an int
which we really don't want.The first step to solving this is to write the interface we wish we were working with. In this case that's quite simple:
public interface INicePrinter
{
void Print(string text);
}
Now we have a nice interface we can change our code to use this interface we need a way of using it. That's where the adaptor comes in:
public class HorriblePrinterAdaptor : INicePrinter
{
public HorriblePrinterAdaptor(IHorriblePrinter horrible)
{
_horrible = horrible
?? throw new ArgumentNullException(nameof(horrible));
}
public void Print(string text)
{
_horrible.Print(text);
}
}
This class is the adaptor. It:
INicePrinter
).IHorriblePrinter
).INicePrinter.Print(string text)
method by calling through to the IHorriblePrinter.Print(string text)
method.The final step is to change our code to use the new class:
IHorriblePrinter printer = ...
IHorriblePrinter horriblePrinter = ...
INicePrinter printer = new HorriblePrinterAdaptor(horriblePrinter);
printer.Print("Some words I want to print!");
Let's revisit our checklist of terrible things:
IHorriblePrinter
❌ - not anymore, they only have access to INicePrinter
.IHorriblePrinter.Print(string text)
method that we want to use returns an int
which we really don't want. ❌ - the adaptor hides this away.