介面卡模式(Adapter)指的是將一個類的介面轉換成另一個可以相容的介面。比如我們日常生活中的轉換頭、古早時期使用的電池萬能充,就相當於程式中使用的介面卡模式。
一、介面卡模式的介紹
1.1 介面卡模式的結構
介面卡模式模式主要分為類結構型模式和物件結構型模式兩種:
1.1.1 類介面卡模式
類介面卡模式通過多重繼承,將一個介面與另一個介面進行匹配。而對於一些面嚮物件語言如C#、Java不支援多重繼承,那麼我們就可以繼承一個類,同時實現多個介面來達到介面卡的效果。如下圖所示:
Adaptee
:適配者類,它是需要被訪問的、需要被適配的元件Target
:目標介面,當前系統業務所使用的介面,可以是抽象類或介面Adapter
:介面卡類,通過繼承和實現目標介面,讓客戶端按照目標介面的方法訪問適配者Client
:客戶端,介面卡的使用者
1.1.2 物件介面卡模式
物件介面卡模式相對於類介面卡的不同點在於,物件介面卡中適配者類和介面卡類的耦合度要更低。如下圖所示:
Adaptee
:適配者類,它是需要被訪問的、需要被適配的元件Target
:目標介面,當前系統業務所使用的介面,可以是抽象類或介面Adapter
:介面卡類,通過聚合和實現目標介面,讓客戶端按照目標介面的方法訪問適配者Client
:客戶端,介面卡的使用者
1.2 介面卡模式的應用實現
我們可以根據上面兩種模式分別進行實現:
1.2.1 類介面卡實現程式碼
//適配者類
public class Adaptee {
public void specificRequest(){
System.out.println("我是適配者類");
}
}
//目標介面
public interface Target {
public void request();
}
//介面卡類
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
specificRequest();
}
}
//客戶端類
public class Client {
public static void main(String[] args) {
Target target = new Adapter();
target.request();
}
}
1.2.2 物件介面卡實現程式碼
//適配者類
public class Adaptee {
public void specificRequest(){
System.out.println("我是適配者類");
}
}
//物件介面卡類
public class ObjectAdapter implements Target{
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
//客戶端類
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
我們發現,物件介面卡模式是在介面卡類中引入了適配者,這樣就利用聚合的方式將兩個類連線在一起。而根據設計原則,聚合優先於繼承,所以在我們日常的使用中,應該多選擇物件介面卡模式。
三、介面卡的應用場景
3.1 MyBatis中的日誌適配應用
在MyBatis 中的典型代表是日誌模組,比如其中適配了slf4j
、Apache Commons Logging
、Log4j2
和JDK logging
等的日誌型別,下面來看看具體實現:
//統一的Log介面
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
MyBatis 定義了多個日誌型別的介面卡,以Log4j2
實現為例:
public class Log4j2Impl implements Log {
private final Log log;
public Log4j2Impl(String clazz) {
Logger logger = LogManager.getLogger(clazz);
if (logger instanceof AbstractLogger) {
log = new Log4j2AbstractLoggerImpl((AbstractLogger) logger);
} else {
log = new Log4j2LoggerImpl(logger);
}
}
@Override
public boolean isDebugEnabled() {
return log.isDebugEnabled();
}
@Override
public boolean isTraceEnabled() {
return log.isTraceEnabled();
}
@Override
public void error(String s, Throwable e) {
log.error(s, e);
}
@Override
public void error(String s) {
log.error(s);
}
@Override
public void debug(String s) {
log.debug(s);
}
@Override
public void trace(String s) {
log.trace(s);
}
@Override
public void warn(String s) {
log.warn(s);
}
}
所以在專案新增Log4j2
後,就可以直接使用它列印MyBatis的日誌資訊。