本文节选自《设计模式就该这样学》1使用类适配器重构第三方登录自由适配我们使用适配模式来实现一个实际的业务场景,解决实际问题。年龄稍大的人一定经历过这样的过程。很早以前开发的老系统本该有登录界面的,但是随着业务的发展和社会的进步,很明显单纯依靠用户名和密码登录已经不能满足用户的需求。现在大部分系统已经支持QQ登录、微信登录、手机登录、微博登录等多种登录方式,同时保留了用户名和密码的登录方式。登录表单虽然丰富,但是登录后的处理逻辑不需要改动,将登录状态保存到Session中,遵循开闭原则。首先创建一个统一的返回结果ResultMsg类。/***由汤姆创建。*/publicclassResultMsg{privateintcode;私有字符串消息;私有对象数据;publicResultMsg(intcode,Stringmsg,Objectdata){this.code=code;this.msg=msg;这个。数据=数据;}publicintgetCode(){返回代码;}publicvoidsetCode(intcode){this.code=code;}publicStringgetMsg(){返回消息;}publicvoidsetMsg(Stringmsg){this.msg=msg;}publicObjectgetData(){返回数据;}publicvoidsetData(Objectdata){this.data=data;}}假设在旧系统中,处理登录逻辑的代码在PassportService类中。publicclassPassportService{/***注册方法*@paramusername*@parampassword*@return*/publicResultMsgregist(Stringusername,Stringpassword){returnnewResultMsg(200,"注册成功",newMember());}/***登录方法*@paramusername*@parampassword*@return*/publicResultMsglogin(Stringusername,Stringpassword){returnnull;}}为了遵循开闭原则,没有修改旧系统的代码。下面开始代码重构之路,创建一个Member类。/***由汤姆创建。*/publicclassMember{私有字符串用户名;私有字符串密码;私有字符串中间;私有字符串信息;publicStringgetUsername(){返回用户名;}publicvoidsetUsername(Stringusername){this.username=username;}publicStringgetPassword(){返回密码;}publicvoidsetPassword(Stringpassword){this.password=password;}publicStringgetMid(){返回中间;}publicvoidsetMid(Stringmid){this.mid=mid;}publicStringgetInfo(){返回信息;}publicvoidsetInfo(Stringinfo){this.info=info;}}我们也不改变动态运行非常稳定的代码,创建Target角色IPassportForThird接口。publicinterfaceIPassportForThird{ResultMsgloginForQQ(StringopenId);ResultMsgloginForWechat(StringopenId);ResultMsgloginForToken(Stringtoken);ResultMsgloginForTelphone(Stringphone,Stringcode);}创建Adapter角色实现兼容,新建类PassportForThirdAdapter,继承原有逻辑。公共类PassportForThirdAdapter扩展PassportService实现IPassportForThird{publicResultMsgloginForQQ(StringopenId){returnloginForRegist(openId,null);}publicResultMsgloginForWechat(StringopenId){returnloginForRegist(openId,null);}publicResultMsgloginForToken(Stringtoken){returnloginForRegist(token,null);}publicResultMsgloginForTelphone(Stringphone,Stringcode){returnloginForRegist(phone,null);}privateResultMsgloginForRegist(Stringusername,Stringpassword){if(null==password){password="THIRD_EMPTY";}super.regist(用户名,密码);返回super.login(用户名,密码);}}客户端测试代码如下。publicstaticvoidmain(String[]args){PassportForThirdAdapter适配器=newPassportForThirdAdapter();adapter.login("tom","123456");adapter.loginForQQ("sjooguwoersdfjhasjfsa");adapter.loginForWechat("slfsjoljsdfs";s)}2使用接口适配器优化代码通过这样一个简单的适配动作,我们实现了代码兼容。当然代码可以更优雅一些,根据不同的登录方式创建不同的Adapter。首先创建LoginAdapter接口。公共接口ILoginAdapter{布尔支持(对象对象);ResultMsglogin(Stringid,Objectadapter);}然后创建一个抽象类AbstractAdapter继承PassportService原有的功能,同时实现ILoginAdapter接口,然后分别实现不同的登录适配,QQ登录LoginForQQAdapter。publicclassLoginForQQAdapterextendsAbstractAdapter{publicbooleansupport(Objectadapter){returnadapterinstanceofLoginForQQAdapter;}publicResultMsglogin(Stringid,Objectadapter){if(!support(adapter)){returnnull;}//accessToken//timereturnsuper.loginForRegist(id,null);}}手机登录LoginForTelAdapter。publicclassLoginForTelAdapterextendsAbstractAdapter{publicbooleansupport(Objectadapter){returnadapterinstanceofLoginForTelAdapter;}publicResultMsglogin(Stringid,Objectadapter){returnsuper.loginForRegist(id,null);}}Token自动登录LoginForTokenAdapter。publicclassLoginForTokenAdapterextendsAbstractAdapter{publicbooleansupport(Objectadapter){}publicResultMsglogin(Stringid,Objectadapter){returnsuper.loginForRegist(id,null);}}微信登录LoginForWechatAdapter。publicclassLoginForWechatAdapterextendsAbstractAdapter{publicbooleansupport(Objectadapter){}publicResultMsglogin(Stringid,Objectadapter){returnsuper.loginForRegist(id,null);}}连接创建合适的配置器PassportForThirdAdapter类,现实目标接口IPassportForThird完成包含。公共类PassportForThirdAdapter实现IPassportForThird{publicResultMsgloginForQQ(StringopenId){returnprocessLogin(openId,LoginForQQAdapter.class);}publicResultMsgloginForWechat(StringopenId){returnprocessLogin(openId,LoginForWechatAdapter.class);}publicResultMsgloginForToken(Stringtoken){returnprocessLogin(token,LoginForTokenAdapter.class);}publicResultMsgloginForTelphone(Stringphone,Stringcode){returnprocessLogin(phone,LoginForTelAdapter.class);}privateResultMsgprocessLogin(Stringid,Classclazz){try{ILoginAdapteradapter=clazz.newInstance();如果(adapter.support(adapter)){returnadapter.login(id,adapter);}}catch(Exceptione){e.printStackTrace();}返回空值;}}客户端测试代码如下。publicstaticvoidmain(String[]args){IPassportForThird适配器=newPassportForThirdAdapter();adapter.loginForQQ("sdfasdfasfasfas");}最后看下图所示的类图。至此,我们在遵循开闭原则的前提下,完全实现了兼容多平台登录的业务场景。当然,目前的设计并不完美,仅供参考。有兴趣的朋友可以继续完善这段代码。比如适配器类中的参数,目前设置为String,改为Object[]应该更合理。学习到这里,相信小伙伴们会有一个疑问:adapter模式和strategy模式好像区别不大?我要强调的是,适配模式主要解决的是功能兼容的问题,单场景适配可能无法与策略模式相提并论。但是复杂的场景适配很容易让大家摸不着头脑。其实,你注意到一个细节了吗?作者在每个适配器类中添加了一个support()方法来判断是否兼容。support()方法的参数类型也是Object,support()来自接口。适配器类的实现逻辑不依赖接口,完全可以去掉ILoginAdapter接口。添加接口只是为了规范代码。上面的代码可以说是策略模式、简单工厂模式和适配器模式的综合应用。关注微信公众号『汤姆炸弹架构』回复“设计模式”获取完整源码。【推荐】汤姆炸弹架构:30个设计模式实战案例(附源码),挑战60W年薪不是梦技术在于分享,我分享我的快乐!如果本文对您有帮助,请关注并点赞;有什么建议也可以留言或私信。您的支持是我坚持创作的动力。关注微信公众号『汤姆炸弹建筑』,获取更多技术干货!