Default Adapter
December 27, 2021在 Java 8 之前,介面只用來定義行為外觀,實作介面時要將全部行為實現,例如,Web 容器環境要實作生命週期傾聽器時要實作的 ServletContextListener
,在 Java EE 8(基於 Java SE 7)之前是這麼定義:
package javax.servlet;
import java.util.EventListener;
public interface ServletContextListener extends EventListener {
public void contextInitialized(ServletContextEvent sce);
public void contextDestroyed(ServletContextEvent sce);
}
或許你只想處理 Web 應用程式初始化,然而兩個方法都得實作,contextDestroyed
沒什麼事,方法本體只好空著:
public class GossipListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
...實作應用程式初始化
}
public void contextDestroyed(ServletContextEvent sce) {}
}
ServletContextListener
只定義了兩個方法,空著一個方法本體也許還好,不過有的介面定義了一大堆方法,若你只對其中一個方法感興趣,其他方法都得空著,程式寫起來麻煩,閱讀時也不方便吧!
Java 8 之前的配接器
在 Java SE 8 之前,有個方式是先定義一個抽象類別來實作該介面,例如:
public abstract class ServletContextAdapter implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {}
public void contextDestroyed(ServletContextEvent sce) {}
}
如果你只想處理 Web 應用程式初始化,繼承 ServletContextAdapter
後只要重新定義 contextInitialized
就可以了:
public class GossipListener extends ServletContextAdapter {
@Override
public void contextInitialized(ServletContextEvent sce) {
...實作應用程式初始化
}
}
ServletContextAdapter
就像個配接器,讓你只處理感興趣的,這算是實現了關注分離吧!
Java 8 以後的配接器
Java SE 8 以後,介面可以定義預設方法,基於 Java 8 的 Java EE 8,ServletContextListener
的定義變成:
package javax.servlet;
import java.util.EventListener;
public interface ServletContextListener extends EventListener {
public default void contextInitialized(ServletContextEvent sce) {}
public default void contextDestroyed(ServletContextEvent sce) {}
}
因為介面允許有預設的方法實作,若只想處理 Web 應用程式初始化,實作 ServletContextListener
時只要重新定義 contextInitialized
就可以了:
public class GossipListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
...實作應用程式初始化
}
}
在過去以抽象類別實作配接器的一些程式庫或框架,若已經採用了 Java SE 8 作為基礎,多半也改用介面預設方法來實現,例如 Spring 的 Web 組態檔必須實作 WebMvcConfigurer
的行為,這個介面要實作的方法蠻多的,在 Spring 5 之前,會繼承 WebMvcConfigurerAdapter
,針對感興趣的行為重新定義。
Spring 5(基於 Java SE 8)以後,WebMvcConfigurer
有定義預設方法,WebMvcConfigurerAdapter
也就被廢棄了。
只有 Java 才需要?
這個模式好像是 Java 才會用?不!就算是動態定型語言也可有這類需求,動態定型語言採用鴨子定型(Duck typing),雖然不用像 Java 一定要用 interface
明確地寫出行為規範,不過還是得遵守行為協定,不然執行時期會引發 AttributeError
或 TypeError
之類的問題。
具有 adapter 字眼的模式,代表著會有一方制定了規範,而另一方必須滿足規範,要滿足規範的那方就是 adapter。
Default Adapter 就是提供預設實作的 adapter,主要概念是,如果你的函式需要一組行為,而客戶端可能只對其中幾個行為感興趣,可以這組行為提供預設動作(不一定是空動作),不強迫客戶端處理他們不感興趣的東西。
因此廣義來說,其實很多地方都看得到 Default Adapter 的概念,例如 JavaScript 的 JSON.stringify
,如果你指定的物件有 toJSON
,就會用 toJSON
的定義轉換為 JSON,不然就有預設動作,不是嗎?
別因為模式有個名稱,就把它當成是個什麼了不起的東西,如果你撰寫程式時,隨時檢視程式碼、視需求變化、維護觀點適當重構與調整…在這個過程中,你可能有些類似的思考方式,那就是模式了。
哪天你跟一群人溝通你的想法並建立了共識,日後為了便於溝通,再想想看有什麼大家都能接受的適當名稱,就只是這樣罷了,思考才是最重要的!