一、適配器模式的應用場景
適配器模式(Adapter Pattern)是指將一個類的接口轉換成用戶期待的另一個接口,使原本接口不兼容的類可以一起工作,屬於構造設計模式。
適配器適用於以下幾種業務場景:
- 已經存在的類的方法和需求不匹配(方法結果相同或相似)的情況。
- 適配器模式不是軟件初始階段應該考慮的設計模式,是隨着軟件的開發,由於不同產品、不同廠家造成功能類似而接口不同的問題的解決方案,有點亡羊補牢的感覺。
二、重構第三方登錄自由適配的業務場景
將原來的單一支持用戶名和密碼登錄,擴展為可以支持微信和手機登錄。
創建統一返回結果ResultMsg:
@Data
public class ResultMsg {
private Integer code;
private String msg;
private Object data;
public ResultMsg(Integer code, String msg, Object data) {
this.code = code;
this.data = data;
this.msg = msg;
}
}
老系統登錄代碼如下:
public class SignInService { public ResultMsg regist(String userName, String passWord) { return new ResultMsg(200, "注冊成功", new Member()); } public ResultMsg login(String userName, String passWord) { return null; } }
為了遵循開閉原則,我們不修改老系統代碼,下面是Member類:
@Data public class Member { private String userName; private String passWord; private String mid; private String info; }
我們優雅的根據不同登錄方式創建不同的“Adapter”,首先創建LoginAdapter:
public interface LoginAdapter { boolean support(Object adapter); ResultMsg login(String id, Object adapter); }
手機登錄:
public class LoginForTelAdapter implements LoginAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForTelAdapter; } @Override public ResultMsg login(String id, Object adapter) { return null; } }
微信登錄:
public class LoginForWechatAdapter implements LoginAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForWechatAdapter; } @Override public ResultMsg login(String id, Object adapter) { return null; } }
接着,創建第三方登錄兼容接口IPassportForThid:
public interface IPassportForThird { ResultMsg loginForTel(String telephone, String code); ResultMsg loginForWechat(String id); ResultMsg loginForResist(String userName, String passWord); }
實現兼容PassportForThirdAdapter:
public class PassportForThirdAdapter extends SignInService implements IPassportForThird { @Override public ResultMsg loginForTel(String telephone, String code) { return null; } @Override public ResultMsg loginForWechat(String id) { return null; } @Override public ResultMsg loginForResist(String userName, String passWord) { super.regist(userName, passWord); return super.login(userName, passWord); } //這里使用簡單工廠及策略模式 private ResultMsg procssLogin(String key, Class<? extends LoginAdapter> clazz) { try { LoginAdapter adapter = clazz.newInstance(); if (adapter.support(adapter)) { return adapter.login(key, adapter); } } catch (Exception e) { e.printStackTrace(); } return null; } }
前面每個適配器都加上了support()方法,用來判斷箭筒。support()方法的參數也是Object類型,而support()來自接口。適配器並不依賴接口,我們使用接口只是為了代碼規范。
三、適配器模式在源碼中的體現
Spring中的AOP中AdvisorAdapter類,它有三個實現:MethodBeforAdviceAdapter、AfterReturnningAdviceAdapter、ThrowsAdviceAdapter。
先看頂層接口AdviceAdapter的源代碼:
public interface AdvisorAdapter { boolean supportsAdvice(Advice var1); MethodInterceptor getInterceptor(Advisor var1); }
再看MethodBeforAdviceAdapter:
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable { private final MethodBeforeAdvice advice; public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); } }
其他兩個類就不看了。Spring會根據不同的AOP配置來使用對應的“Advice”,與策略模式不同的是,一個方法可以同時擁有多個“Advice”。
四、適配器模式的優缺點
優點:
- 能提高類的透明性和復用性,現有的類會被復用但不需要改變。
- 目標類和適配器類解耦,可以提高程序的擴展性。
- 在很多業務場景中符合開閉原則。
缺點:
- 在適配器代碼編寫過程中需要進行全面考慮,可能會增加系統復雜度。
- 增加代碼閱讀難度,過多使用適配器會使系統代碼變得凌亂。