Lets assume that in your current codebase, there exists MyLogger
interface like so:
interface MyLogger {
void logMessage(String message);
void logException(Throwable exception);
}
Lets say that you've created a few concrete implementations of these, such as MyFileLogger
and MyConsoleLogger
.
You have decided that you want to use a framework for controlling your application's Bluetooth connectivity. This framework contains a BluetoothManager
with the following constructor:
class BluetoothManager {
private FrameworkLogger logger;
public BluetoothManager(FrameworkLogger logger) {
this.logger = logger;
}
}
The BluetoothManager
also accepts a logger, which is great! However it expects a logger of which the interface was defined by the framework and they have used method overloading instead of naming their functions differently:
interface FrameworkLogger {
void log(String message);
void log(Throwable exception);
}
You already have a bunch of MyLogger
implementations that you would like to reuse, but they do not fit the interface of the FrameworkLogger
. This is where the adapter design-pattern comes in:
class FrameworkLoggerAdapter implements FrameworkLogger {
private MyLogger logger;
public FrameworkLoggerAdapter(MyLogger logger) {
this.logger = logger;
}
@Override
public void log(String message) {
this.logger.logMessage(message);
}
@Override
public void log(Throwable exception) {
this.logger.logException(exception);
}
}
By defining an adapter class that implements the FrameworkLogger
interface and accepts a MyLogger
implementation the functionality can be mapped between the different interfaces. Now it is possible to use the BluetoothManager
with all of the MyLogger
implementations like so:
FrameworkLogger fileLogger = new FrameworkLoggerAdapter(new MyFileLogger());
BluetoothManager manager = new BluetoothManager(fileLogger);
FrameworkLogger consoleLogger = new FrameworkLoggerAdapter(new MyConsoleLogger());
BluetoothManager manager2 = new BluetoothManager(consoleLogger);