使用類介面卡重構第三方登入自由適配

2020-10-13 01:00:28
在《介面卡模式詳解》一節我們已經瞭解了介面卡模式,下面使用類介面卡來實現一個實際的業務場景,解決實際問題。

年紀稍微大一點的小夥伴一定知道,很早以前開發的老系統大部分都有登入介面。隨著業務的發展和社會的進步,單純的依賴使用者名稱和密碼登入顯然已經不能滿足使用者的需求了。

現在,大部分系統都支援多種登入方式,如 QQ 登入、微信登入、手機登入、微博登入、自動登入等。雖然登入形式豐富,但是登入後的處理邏輯不用變,都是將登入狀態儲存到 Session,遵循開閉原則。

首先建立統一的返回結果 ResultMsg 類:
public class ResultMsg {

    private int code;
    private String msg;
    private Object data;

    public ResultMsg(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    // 省略set和get方法
}
假設在老系統中,處理登入邏輯的程式碼在 SignService 類中。
public class SignService {

    /**
     * 註冊方法
     *
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password) {
        return new ResultMsg(200, "註冊成功", new user());
    }

    /**
     * 登入方法
     *
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password) {
        return null;
    }

}
為了遵循開閉原則,不修改老系統的程式碼。下面開啟程式碼重構之路,建立 User 類。
public class User {

    private String username;
    private String password;
    private String mid;
    private String info;

    // 省略set和get方法
}
我們也不改動執行非常穩定的程式碼,建立目標介面角色 ISignForThird。
public interface ISignForThird {
    ResultMsg loginForQQ(String openId);

    ResultMsg loginForWechat(String openId);

    ResultMsg loginForToken(String token);

    ResultMsg loginForTelphone(String phone, String code);
}
增加 Adapter 角色實現相容。建立一個新的類 SignForThirdAdapter,繼承原來的邏輯,執行非常穩定的程式碼我們不去改動。
public class SignForThirdAdapter extends SignService implements ISignForThird {

    public ResultMsg loginForQQ(String openId) {
        return loginForRegist(openId, null);
    }

    public ResultMsg loginForWechat(String openId) {
        return loginForRegist(openId, null);
    }

    public ResultMsg loginForToken(String token) {
        return loginForRegist(token, null);
    }

    public ResultMsg loginForTelphone(String phone, String code) {
        return loginForRegist(phone, null);
    }

    private ResultMsg loginForRegist(String username, String password) {
        if (null == password) {
            password = "THIRD_EMPTY";
        }
        super.register(username, password);
        return super.login(username, password);
    }
}
使用者端測試程式碼如下:
public static void main(String[] args) {
    SignForThirdAdapter adapter = new SignForThirdAdapter();
    adapter.login("C語言中文網", "123456");
    adapter.loginForQQ("1154852654");
    adapter.loginForWechat("455256225");
}
這樣通過這麼一個簡單的適配動作,我們完成了程式碼相容。當然,程式碼還可以更加優雅。

使用介面​介面卡優化程式碼

下面根據不同的登入方式,建立不同的 Adapter。首先建立 LoginAdapter 介面。
public interface ILoginAdapter {
    boolean support(Object object);

    ResultMsg login(String id, Object adapter);
}
然後建立一個抽象類 AbstractAdapter 繼承 SignService 原有的功能,同時實現 ILoginAdapter 介面,再分別實現不同的登入適配。
public abstract class AbstractAdapter extends SignService implements ILoginAdapter {
    protected ResultMsg loginForRegist(String username, String password) {
        if (null == password) {
            password = "THIRD_EMPTY";
        }
        super.register(username, password);
        return super.login(username, password);
    }
}
QQ 登入 LoginForQQAdapter 如下。
public class LoginForQQAdapter extends AbstractAdapter {
    public boolean support(Object adapter) {
        return adapter instanceof LoginForQQAdapter;
    }

    public ResultMsg login(String id, Object adapter) {
        if (!support(adapter)) {
            return null;
        }
        //accesseToken
        //time
        return super.loginForRegist(id, null);
    }

}
手機登入 LoginForTelAdapter 如下。
public class LoginForTelAdapter extends AbstractAdapter {
    public boolean support(Object adapter) {
        return adapter instanceof LoginForTelAdapter;
    }

    public ResultMsg login(String id, Object adapter) {
        return super.loginForRegist(id, null);
    }

}
Token 自動登入 LoginForTokenAdapter 如下。
public class LoginForTokenAdapter extends AbstractAdapter {
    public boolean support(Object adapter) {
        return adapter instanceof LoginForTokenAdapter;
    }

    public ResultMsg login(String id, Object adapter) {
        return super.loginForRegist(id, null);
    }

}
微信登入 LoginForWechatAdapter 如下。
public class LoginForWechatAdapter extends AbstractAdapter {
    public boolean support(Object adapter) {
        return adapter instanceof LoginForWechatAdapter;
    }

    public ResultMsg login(String id, Object adapter) {
        return super.loginForRegist(id, null);
    }
   
}
接著建立介面卡 SignForThirdAdapter 類,實現目標介面 ISignForThird 完成相容。
public class SignForThirdAdapter extends SignService implements ISignForThird {

    public ResultMsg loginForQQ(String openId) {
        return loginForRegist(openId, null);
    }

    public ResultMsg loginForWechat(String openId) {
        return loginForRegist(openId, null);
    }

    public ResultMsg loginForToken(String token) {
        return loginForRegist(token, null);
    }

    public ResultMsg loginForTelphone(String phone, String code) {
        return loginForRegist(phone, null);
    }

    private ResultMsg loginForRegist(String username, String password) {
        if (null == password) {
            password = "THIRD_EMPTY";
        }
        super.register(username, password);
        return super.login(username, password);
    }
}
使用者端測試程式碼如下。
public static void main(String[] args) {
        ISignForThird adapter = new SignForThirdAdapter();
        adapter.loginForQQ("ssfsfafxzsds");
}
最後來看如下圖所示的類圖: