适配器模式
概述
适配器模式(Adapter Pattern)是指将一个类的接口转化为客户希望的另一个接口,是的原本由于不兼容而不能一起工作的接口可以一起工作。简单来说就是需要的东西就在眼前,但却不能使用,而改造它花费的代价又很大,于是就想办法去适配它。其实生活中有很多例子,比如电源适配器,有些国家是110V电压,而我国是220V电压,但我们使用的电器,比如笔记本电脑不是什么电压都可以用的,所以电脑会有自己的电压适配器,不过电压多少电源适配器都会把他转化为我们需要的电压。
适配器模式主要适用于当需要复用现有的类,但是接口又与现在的环境不匹配,而现有的类已经稳定运行的情况。在“GOF的设计模式中,对适配器讲了两种类型,类适配器模式和对象适配器模式,由于类适配器模式是通过多重继承对一个接口与另一个接口进行匹配,而JAVA等语言都不支持多重继承(C++支持),也就是类只有一个父类,所以我们这里主要讲对象适配器模式”
Target(目标接口,客户端所期待的接口)
public interface Target { public void request(); }
Adaptee(需要适配的类)
public class Adaptee { public void specificRequest() { System.out.println("特殊请求"); } }
Adapter(通过内部包装一个Adaptee,把源接口转换成目标接口)
public class Adapter implements Target{ private Adaptee adaptee = new Adaptee(); @Override public void request() { this.adaptee.specificRequest(); } }
客户端代码
public class Client { public static void main(String[] args) { Target target = new Adapter(); target.request(); } }
在想使用一个已经存在的类,但如果他的接口,也就是它的方法和你的要求不同时就可以考虑适配器模式,其实适配器有点‘亡羊补牢‘的感觉,不过没办法是软件就避免不了要维护,一般我们会在软件后期或者维护期去使用,在公司开发时类和方法命名应该有规范,最好前期设计好,如果真的有接口不同首先考虑的应该是通过重构统一接口,就是在双方都不容易修改时使用适配器模式,当然也有设计之初考虑适配器的时候,当我们需要考虑使用第三方开发组件,而这个组件接口与我们自己的接口不相同时,没必要为了迎合它而改变自己的接口时可以考虑适配器模式。
我们再举个例子,我们知道以前做系统登录只有一种就是注册与登录,使用用户名和密码登录,但是现在我们增加了第三方登录比如微信登录等,我们需要系统支持第三方登录,同时不能修改原有的已经稳定运行的功能
首先基础类
Member(成员类)
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; } }
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; } 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; } }
版本一
原有登录方法,运行稳定我们不去修改
public class LoginService { //注册方法 public ResultMsg register(String username,String password) { return new ResultMsg(200,"注册成功",new Member()); } //登录方法 public ResultMsg login(String username,String password) { return new ResultMsg(200,"登录成功",new Member()); } }
第三方登录-适配器
public class LoginThirdService { private LoginService loginService; public LoginThirdService(LoginService loginService) { this.loginService = loginService; } public ResultMsg loginForWechat(String openId) { return registerAndLogin(openId,null); } public ResultMsg loginForToken(String token) { //通过token获取用户名密码再重新登录 return null; } public ResultMsg registerAndLogin(String username,String password) { loginService.register(username,password); return loginService.login(username,password); } }
版本二
结合简单工厂模式和适配器模式使得代码更加优雅
抽象适配器类
public abstract class LoginAdapter { protected LoginService loginService = new LoginService(); abstract boolean support(Object adapter); abstract ResultMsg login(String id,Object adapter); }
微信登录适配
public class LoginForWeChatAdapter extends LoginAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForWeChatAdapter; } @Override public ResultMsg login(String username,Object adapter) { super.loginService.register(username,null); return super.loginService.login(username,null); } }
QQ登录适配
public class LoginForQQAdapter extends LoginAdapter { @Override public boolean support(Object adapter) { return adapter instanceof LoginForQQAdapter; } @Override public ResultMsg login(String username,Object adapter) { super.loginService.register(username,null); return super.loginService.login(username,null); } }
第三方登录接口
public interface ILoginForThird { ResultMsg LoginForWeChat(String id); ResultMsg LoginForQQ(String id); }
接口实现
public class LoginForThirdImpl implements ILoginForThird { @Override public ResultMsg LoginForWeChat(String id) { return processLogin(id,LoginForWeChatAdapter.class); } @Override public ResultMsg LoginForQQ(String id) { return processLogin(id,LoginForQQAdapter.class); } private ResultMsg processLogin(String id,Class<? extends LoginAdapter> clazz) { try { LoginAdapter loginAdapter = clazz.newInstance(); if (loginAdapter.support(loginAdapter)) return loginAdapter.login(id,loginAdapter); }catch (Exception e) { e.printStackTrace(); } return null; } }
大概的写一下这个过程代码还有很多优化的地方,我们发现适配器模式可以解决兼容问题,是不是意味着我们可以经常性使用它呢?其实如果我们在设计之初就能预防接口不同的问题,接口匹配问题就不会发生,在有小的接口不统一时,及时重构,问题就不会扩大;只有碰到无法改变原有代码时考虑使用