適配器模式學習

1. 定義理解

當你想使用一個已經存在的類,而它的接口不符合你的需求,或者你想建立一個可重用的類(與不兼容接口無關的類),這時候能夠考慮使用適配器模式。同時它也是一種包裝模式,它與裝飾模式一樣具備包裝的功能java


2. 適配器類型

  1. 類適配器 :對象繼承的方式,靜態的定義
  2. 對象適配器 :依賴於對象的組合(在構造方法中實例化成員對象)

3. 代碼示例

3.1 類適配器

  1. 首先定義M4DataLine 表明是Micro USB,咱們目的就是經過適配器可以用米4數據線鏈接米5手機
    class M4DataLine {
        public void connection() {
            System.out.println("使用小米4數據線鏈接...");
        }
    }
  2. 定義客戶端使用的接口,與業務相關
    interface Target {
        void connection();
    }
    
    class M5DataLine implements Target {
        @Override
        public void connection() {
            System.out.println("使用小米5數據線鏈接...");
        }
    }
  3. 建立適配器類,繼承了被適配類,同時實現標準接口
    class M5DataLineAdapter extends M4DataLine implements Target {
    
        @Override
        public void connection() {
            System.out.println("插入 type-c 轉接頭");
            super.connection();
        }
    }
  4. 客戶端代碼,測試
    public class AdapterMain {
    
        public static void main(String[] args) {
            Target target = new M5DataLine();
            target.connection();
    
            Target adapter = new M5DataLineAdapter();
            adapter.connection();
        }
    }
  5. 結果
    使用小米5數據線鏈接...
    插入 type-c 轉接頭
    使用小米4數據線鏈接...

3.2 對象適配器

  1. 建立適配器類,實現標準接口,將這個調用委託給實現新接口的對象來處理
    class M5DataLineAdapter implements Target {
    
        private Target target;
    
        public M5DataLineAdapter(Target target) {
            this.target = target;
        }
    
        @Override
        public void connection() {
            System.out.println("插入 type-c 轉接頭");
            target.connection();
        }
    }
    
    public class AdapterMain {
    
        public static void main(String[] args) {
            // 使用特殊功能類,即適配類
            Target adapter = new M5DataLineAdapter(new M5DataLine());
            adapter.connection();
        }
    }

4. JDK 中的適配器使用

使用適配器模式的類編程

java.util.Arrays#asList()
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
  • Java I/O 庫大量使用了適配器模式,如 ByteArrayInputStream 是一個適配器類,它繼承了 InputStream 的接口,而且封裝了一個 byte 數組。換言之,它將一個 byte 數組的接口適配成 InputStream 流處理器的接口。
  • 在 OutputStream 類型中,全部的原始流處理器都是適配器類。ByteArrayOutputStream 繼承了 OutputStream 類型,同時持有一個對 byte 數組的引用。它一個 byte 數組的接口適配成 OutputString 類型的接口,所以也是一個對象形式的適配器模式的應用
  • FileOutputStream 繼承了 OutputStream 類型,同時持有一個對 FileDiscriptor 對象的引用。這是一個將 FileDiscriptor 接口適配成 OutputStream 接口形式的對象型適配器模式
  • Reader 類型的原始流處理器都是適配器模式的應用。StringReader 是一個適配器類,StringReader 類繼承了 Reader 類型,持有一個對 String 對象的引用。它將 String 的接口適配成 Reader 類型的接口。

5. Spring 中使用適配器模式的典型應用

  1. 在 Spring 的 AOP 裏經過使用的 Advice(通知)來加強被代理類的功能。Spring 實現這一 AOP 功能的原理就使用代理模式(一、JDK 動態代理。二、CGLib 字節碼生成技術代理。)對類進行方法級別的切面加強,即,生成被代理類的代理類,並在代理類的方法前,設置攔截器,經過執行攔截器中的內容加強了代理方法的功能,實現的面向切面編程。
  2. Advice(通知)的類型有:BeforeAdvice、AfterReturningAdvice、ThrowSadvice 等。每一個類型 Advice(通知)都有對應的攔截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。Spring 須要將每一個 Advice(通知)都封裝成對應的攔截器類型,返回給容器,因此須要使用適配器模式對 Advice 進行轉換

如下是源碼示例:數組

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method method, Object[] args, Object target) throws Throwable;
}

public interface AdvisorAdapter {

    boolean supportsAdvice(Advice advice);

    MethodInterceptor getInterceptor(Advisor advisor);
}

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}

默認的適配器註冊表:ide

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);

    public DefaultAdvisorAdapterRegistry() {
        // 註冊適配器 
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }

    @Override
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // So well-known it doesn't even need an adapter.
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // 檢查是否支持,這裏調用了適配器的方法 
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }

    @Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // 檢查是否支持,這裏調用了適配器的方法 
            if (adapter.supportsAdvice(advice)) {
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
    }

    @Override
    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }

}

6. 優缺點

  1. 可讓任何兩個沒有關聯的類協同工做
  2. 提升了類的複用,想使用現有的類,而此類的接口標準又不符合現有系統的須要。經過適配器模式就可讓這些功能獲得更好的複用。
  3. 增長了類的透明度,客戶端只關注結果
  4. 使用適配器的時候,能夠調用本身開發的功能,從而天然地擴展系統的功能
  5. 缺點:過多使用會致使系統凌亂,追溯困難(內部轉發致使,調用A適配成B)

7. 總結

  1. 建議儘可能使用對象的適配器模式,少用繼承。
  2. 適配器模式屬於補償模式,專門用來在系統後期擴展、修改時使用
  3. 不可過分使用
  4. 兼有裝飾模式的包裝效果,又兼有代理的委託效果
相關文章
相關標籤/搜索