This is the 12th day of my participation in the August Challenge


1. Log adapter

I used logging as an example when I wrote about “strategic patterns,” and this article will use logging as an example. The importance of logging will not be discussed here. When logging in system development, have you ever considered the question: what if the logging framework you use needs to be changed? What do you need to do once you change? Change all the code, replace the Logger class? Doing so would be a disaster for both the system and the developer. Is there a more elegant way to do this?

From the beginning of the system design, developers need to take this into account: encapsulating logging abstractions and adapting them to third-party logging frameworks. To prevent future disasters when the logging framework is replaced.

I don’t care how the third party logging framework is implemented, I just need to define what our system logging needs to do, and if we’re going to introduce a third party library, adapt it to what our system needs.

In fact, SLF4J does just that. It does not provide logging itself, but is a “simple logging facade” that ADAPTS the tripartite library to the logging interface it defines. This article will not discuss the implementation of SLF4J.

Now suppose the system might need three logging implementations: JDK logging, Log4j, and Logback. Let’s try to describe the adaptation process in code. The class diagram is as follows:writeLoggerInterface to define the functions of logging:

public interface Logger {

	void info(String message);

	void error(String message, Throwable t);
}
Copy the code

JdkLoggerAdapter ADAPTS JDK logs:

public class JdkLoggerAdapter implements Logger{
	private java.util.logging.Logger logger;

	// omit the constructor

	@Override
	public void info(String message) {
		logger.log(Level.INFO, message);
	}

	@Override
	public void error(String message, Throwable t) { logger.log(Level.SEVERE, message, t); }}Copy the code

Log4jLoggerAdapter ADAPTS to Log4j logs:

public class Log4jLoggerAdapter implements Logger{
	private org.apache.log4j.Logger logger;

	// omit the constructor

	@Override
	public void info(String message) {
		logger.info(message);
	}

	@Override
	public void error(String message, Throwable t) { logger.error(message, t); }}Copy the code

LogbackLoggerAdapter ADAPTS Logback logs similarly, so the code is not attached.

Define log enumeration:

public enum LogEnum {
	LOG4J,
	LOGBACK,
	JDK;
}
Copy the code

How to gracefully generate log objects? The log factory, of course.

public class LoggerFactory {
	private static LogEnum logEnum;

	public static void setLogEnum(LogEnum logEnum) {
		LoggerFactory.logEnum = logEnum;
	}

	public static Logger getLogger(Class
        c) {
		switch (logEnum) {
			case JDK:
				return new JdkLoggerAdapter(java.util.logging.Logger.getLogger(c.getName()));
			case LOG4J:
				return new Log4jLoggerAdapter(org.apache.log4j.Logger.getLogger(c));
			case LOGBACK:
				return new LogbackLoggerAdapter(new LoggerContext().getLogger(c));
			default:
				return null; }}}Copy the code

The client calls like this:

public class Client {
	public static void main(String[] args) throws Exception {
		LoggerFactory.setLogEnum(LogEnum.JDK);
		Logger logger = LoggerFactory.getLogger(Client.class);
		logger.info("The JDK log..."); }}Copy the code

See, the client doesn’t care what the underlying logging framework is. It just relies on the logging abstraction, knowing that there are five ways to log. You just adapt the tripartite library to the interface I want, and I’ll just call it. This is the adapter pattern!

2. Definition of the adapter pattern

Transform the interface of one class into another that the client expects, so that two classes that would otherwise not work together because of interface mismatches can work together.

Adapter pattern generic class diagram

  • Target: The Target interface, which is the desired interface that the adapter needs to convert other interfaces into.
  • Adapter: Adapter role, the core of the Adapter pattern.
  • Adaptee: The source role, the person doing the actual work that the adapter needs to convert to the target interface.

Adapters are also common in life, such as power adapters, 3.5mm to Type-C headphone cables and so on. Simply put, the adapter pattern transforms an interface or class into another interface or class. As shown below, two graphs that do not work together can now work together through the addition of an adapter.

3. Advantages of the adapter pattern

  1. You can make two classes that would otherwise not work together work together without modifying the source code, in accordance with the “open closed principle.”
  2. The high-level module shields the low-level implementation, and the adapter encapsulates the source object.
  3. Improved class reusability.
  4. Very flexible, adapters are non-intrusive to existing code, existing objects do not need to know that adapters exist, when they do not want to delete them.

Whenever you want to modify an interface that is already up and running, you can consider using “adapter mode.”

4. To summarize

The “adapter pattern” is a remedy for interfaces that are already online. If modifying the source code is risky, consider using the adapter pattern to make adjustments. Forget about it at the beginning of the system design.