How to quickly handle third-party login and easy to expand?

This article is excerpted from "design patterns should be learned this way"

1. Use class adapter to reconstruct the free adaptation of third-party login

We use the adaptation pattern to implement an actual business scenario and solve practical problems. A little older partner must have experienced this process. The old systems developed long ago should have login interfaces, but with the development of business and the progress of society, simply relying on user name and password login obviously can not meet the needs of users. Now, most systems have supported a variety of login methods, such as QQ login, wechat login, mobile phone login, microblog login, etc., while retaining the login method of user name and password. Although the login forms are rich, the processing logic after login does not need to be changed. The login status is saved to Session and follows the opening and closing principle. First, create a unified ResultMsg class that returns results.

/**
 * Created by Tom.
 */
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;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

Suppose that in the old system, the code for processing login logic is in the passport service class.

public class PassportService {

    /**
     * Registration method
     * @param username
     * @param password
     * @return
     */
    public ResultMsg regist(String username,String password){
        return  new ResultMsg(200,"login was successful",new Member());
    }


    /**
     * Login method
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username,String password){
        return null;
    }

}

In order to follow the opening and closing principle, the code of the old system is not modified. Next, open the path of code refactoring and create the Member class.

/**
 * Created by Tom.
 */
public class Member {

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getMid() {
        return mid;
    }

    public void setMid(String mid) {
        this.mid = mid;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

We also do not change the code that runs very stably, and create the IPassportForThird interface of the Target role.

public interface IPassportForThird {

    ResultMsg loginForQQ(String openId);

    ResultMsg loginForWechat(String openId);

    ResultMsg loginForToken(String token);

    ResultMsg loginForTelphone(String phone,String code);

}

Create an Adapter role to achieve compatibility. Create a new class, passport for third Adapter, and inherit the original logic.

public class PassportForThirdAdapter extends PassportService implements IPassportForThird {

    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.regist(username,password);
        return super.login(username,password);
    }
}

The client test code is as follows.

public static void main(String[] args) {
        PassportForThirdAdapter adapter = new PassportForThirdAdapter();
        adapter.login("tom","123456");
        adapter.loginForQQ("sjooguwoersdfjhasjfsa");
        adapter.loginForWechat("slfsjoljsdo8234ssdfs");
}

2 optimize code using interface adapter

Through such a simple adaptation action, we have completed code compatibility. Of course, the code can be more elegant and create different adapters according to different login methods. First create the LoginAdapter interface.

public interface ILoginAdapter {
    boolean support(Object object);
    ResultMsg login(String id,Object adapter);
}

Then create an abstract class AbstractAdapter, which inherits the original functions of the passport service, implements the ILoginAdapter interface, and then implements different login adaptations, QQ login 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);

    }

}

Log in to loginfortaltadapter from your mobile phone.

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 automatically logs in to 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);
    }
}

Log in to LoginForWechatAdapter via wechat.

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);
    }
}

Then create the adapter passportforthirdapter class to realize the compatibility of the target interface IPassportForThird.

public class PassportForThirdAdapter implements IPassportForThird {

    public ResultMsg loginForQQ(String openId) {
        return processLogin(openId, LoginForQQAdapter.class);
    }

    public ResultMsg loginForWechat(String openId) {

        return processLogin(openId, LoginForWechatAdapter.class);

    }

    public ResultMsg loginForToken(String token) {

        return processLogin(token, LoginForTokenAdapter.class);
    }

    public ResultMsg loginForTelphone(String phone, String code) {
        return processLogin(phone, LoginForTelAdapter.class);
    }


    private ResultMsg processLogin(String id,Class<? extends ILoginAdapter> clazz){
        try {
            ILoginAdapter adapter = clazz.newInstance();
            if (adapter.support(adapter)){
                return adapter.login(id,adapter);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
}

The client test code is as follows.

public static void main(String[] args) {
        IPassportForThird adapter = new PassportForThirdAdapter();
        adapter.loginForQQ("sdfasdfasfasfas");
}

Finally, let's look at the class diagram shown in the figure below.

So far, on the premise of following the opening and closing principle, we have completely realized a business scenario compatible with multi platform login. Of course, the current design is not perfect, for reference only. Interested partners can continue to improve this code. For example, the parameter in the adapter class is currently set to String. It should be more reasonable to change it to Object [].

After learning this, I believe my friends will have a question: does the adapter mode seem to be little different from the strategy mode? I would like to emphasize that the adapter mode mainly solves the problem of function compatibility. Single scenario adaptation may not be compared with the policy mode. But complex scene adaptation is easy to be confused. In fact, have you found a detail? The author adds a support() method to each adapter class to judge whether it is compatible. The parameter type of the support() method is also Object, and the support() comes from the interface. The implementation logic of the adapter class does not depend on the interface, and the ILoginAdapter interface can be removed. The interface is added only for code specification. The above code can be said to be the comprehensive application of policy mode, simple factory mode and adapter mode.

[recommendation] Tom bomb architecture: collecting this article is equivalent to collecting a book on "design patterns"

This article is the original of "Tom bomb architecture". Please indicate the source for reprint. Technology lies in sharing, I share my happiness! undefined if this article is helpful to you, you are welcome to pay attention and praise; If you have any suggestions, you can also leave comments or private letters. Your support is the driving force for me to adhere to my creation. Focus on WeChat official account Tom structure, get more dry cargo!

Posted by lobo235 on Wed, 03 Nov 2021 00:02:50 -0700