Java讀源代碼學設計模式:適配器Adapter
適配器模式相關源代碼:slf4j-1.6.1、hibernate-3.6.7
大家都知道。log4j是一個廣泛使用的日誌工具,除此之外。sun公司在JDK中也有自己的日誌工具,也就是java.util.logging.Logger。
當然還有其它一些日誌工具。
多種日誌工具功能和使用方式相似,一般都包括debug、info、warn、error等日誌級別的方法,但卻沒有實現共同的接口。這一點不像JDBC。盡管關系型數據庫種類非常多。比如MySQL、Oracle等,可是有一套統一的接口,也就是JDBC。
當然。假設你自己寫一個項目,僅僅要隨便找一個日誌工具用即可。可是,一些開源框架的作者就比較糾結了。他不知道你的系統用的是哪種日誌工具。就不知道他在開源框架中使用哪一個日誌工具。
slf4j提供了一個共同的接口。並實現了不同日誌工具的適配器。所以開源框架中日誌僅僅須要調用slf4j提供的接口即可,不須要關心究竟是用的哪個實現類。比如ORM框架Hibernate的日誌就依賴slf4j。
和slf4j相似的還有commons-logging等。
源代碼(因為源代碼巨長,所下面面省略無關代碼):
slf4j提供統一的接口org.slf4j.Logger,該接口提供給client(如Hibernate)去調用:
package org.slf4j; public interface Logger { public boolean isTraceEnabled(); public void trace(String msg); public void trace(String format, Object arg); public void trace(String format, Object arg1, Object arg2); public void trace(String format, Object[] argArray); public void trace(String msg, Throwable t); public boolean isDebugEnabled(); public void debug(String msg); public void debug(String format, Object arg); public void debug(String format, Object arg1, Object arg2); public void debug(String format, Object[] argArray); public void debug(String msg, Throwable t); public boolean isInfoEnabled(); public void info(String msg); public void info(String format, Object arg); public void info(String format, Object arg1, Object arg2); public void info(String format, Object[] argArray); public void info(String msg, Throwable t); public boolean isWarnEnabled(); public void warn(String msg); public void warn(String format, Object arg); public void warn(String format, Object[] argArray); public void warn(String format, Object arg1, Object arg2); public void warn(String msg, Throwable t); public boolean isErrorEnabled(); public void error(String msg); public void error(String format, Object arg); public void error(String format, Object arg1, Object arg2); public void error(String format, Object[] argArray); public void error(String msg, Throwable t); }
在clienthibernate中不是直接調用log4j或JDK logger。而是使用org.slf4j.Logger接口。任取hibernate中有日誌的一段代碼:
(註:LoggerFactory.getLogger怎樣實現本文不須要關註)
package org.hibernate.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class SessionFactoryImpl implements SessionFactory, SessionFactoryImplementor { private static final Logger log = LoggerFactory.getLogger(SessionFactoryImpl.class); private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { log.trace("deserializing"); in.defaultReadObject(); log.debug("deserialized: " + uuid); } private void writeObject(ObjectOutputStream out) throws IOException { log.debug("serializing: " + uuid); out.defaultWriteObject(); log.trace("serialized"); } }
而log4j以及JDK的logger並沒有實現slf4j的org.slf4j.Logger接口,所以slf4j要提供適配器來實現org.slf4j.Logger接口。由適配器去調用log4j或JDK的logger去實現日誌,從而實現日誌工具兼容。(註意:源代碼中能夠看出LocationAwareLogger接口繼承org.slf4j.Logger所以實現LocationAwareLogger相當於實現了org.slf4j.Logger)
Log4j適配器org.slf4j.impl.Log4jLoggerAdapter:
package org.slf4j.impl; import java.io.Serializable; import org.apache.log4j.Level; import org.slf4j.Logger; import org.slf4j.Marker; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; import org.slf4j.spi.LocationAwareLogger; public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger, Serializable { final transient org.apache.log4j.Logger logger; public boolean isDebugEnabled() { return logger.isDebugEnabled(); } public void debug(String msg) { logger.log(FQCN, Level.DEBUG, msg, null); } public void debug(String format, Object arg) { if (logger.isDebugEnabled()) { FormattingTuple ft = MessageFormatter.format(format, arg); logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable()); } } public void debug(String format, Object arg1, Object arg2) { if (logger.isDebugEnabled()) { FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable()); } } public void debug(String format, Object[] argArray) { if (logger.isDebugEnabled()) { FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable()); } } public void debug(String msg, Throwable t) { logger.log(FQCN, Level.DEBUG, msg, t); } }
Jdk logger適配器org.slf4j.impl.JDK14LoggerAdapter:
package org.slf4j.impl; import java.util.logging.Level; import org.slf4j.Marker; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MarkerIgnoringBase; import org.slf4j.helpers.MessageFormatter; import org.slf4j.spi.LocationAwareLogger; public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger { final java.util.logging.Logger logger; public boolean isDebugEnabled() { return logger.isLoggable(Level.FINE); } public void debug(String msg) { if (logger.isLoggable(Level.FINE)) { log(SELF, Level.FINE, msg, null); } } public void debug(String format, Object arg) { if (logger.isLoggable(Level.FINE)) { FormattingTuple ft = MessageFormatter.format(format, arg); log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); } } public void debug(String format, Object arg1, Object arg2) { if (logger.isLoggable(Level.FINE)) { FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); } } public void debug(String format, Object[] argArray) { if (logger.isLoggable(Level.FINE)) { FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); } } public void debug(String msg, Throwable t) { if (logger.isLoggable(Level.FINE)) { log(SELF, Level.FINE, msg, t); } } }
在適配器模式中,一般包括下面幾個部分:
Adaptee:真正實現功能的實現類,可是與client不兼容。也就是上面代碼中的java.util.logging.Logger、org.apache.log4j.Logger。
Target:給client調用的接口,適配器實現這個接口。就是上面代碼中的org.slf4j.Logger。
Adapter:適配器,適配器實現Target接口,詳細功能調用Adaptee來實現。就是上面的org.slf4j.impl.Log4jLoggerAdapter、org.slf4j.impl.JDK14LoggerAdapter。
Client:調用Target接口。
就是上面的Hibernate。
總結:
有多個相似的類(比如java.util.logging.Logger、org.apache.log4j.Logger),沒有統一的接口,可是client又都想要兼容。遇到這樣的情況,最好的辦法是重構,也就是讓他們實現同一接口。可是假設重構成本太大或者根本就無法實現同一接口(比如上面的樣例。log4j和JDK logger根本就不是一家的),就必須創造出統一的接口(即org.slf4j.Logger)。並為每一個類寫一個適配器(即org.slf4j.impl.Log4jLoggerAdapter、org.slf4j.impl.JDK14LoggerAdapter)。讓適配器來實現統一的接口,並調用詳細的實現類來實現,已達到兼容的目的。
作者:叉叉哥 轉載請註明出處:http://blog.csdn.net/xiao__gui/article/details/32695647
Java讀源代碼學設計模式:適配器Adapter