定义
适配器模式(Adapter Pattern)又叫做变压器模式,它的功能是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配二导致无法在以前工作的两个类,能够一起工作。在不改变原有代码的情况下,对原有功能的一种转换、扩展,使得原有的功能能够满足新的需求。
三种写法
- 类适配器
- 对象适配器
- 接口适配器
一、类适配器(系统存在的角色转换成目标需要的内容)
场景说明:现有220V电压。需求是可以输出5V电压同时也可以输出220V电压。
需要通过220V电压转换成5V电压输出,因此在不改变220V电压类的情况下,
增加5V电压。新增5V电压接口,新增适配器类,适配器类继承220V电压,实现5V电压接口。
在适配器类处理220V转换5V的逻辑,最终实现可以输出5V电压。
/**
* 原有类
*/
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输出电压" + output + "V");
return output;
}
}
/**
* 新增方法(模板类)
*/
public interface DC5 {
int output5V ();
}
/**
* 类适配器类
*/
public class PowerAdapter extends AC220 implements DC5 {
@Override
public int output5V() {
int adapterInput = super.outputAC220V();
int adapterOutput = adapterInput / 44;
System.out.println("使用Adapter输入AC" + adapterInput + "V,输出AC"+adapterOutput+"V");
return adapterOutput;
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
DC5 adapter = new PowerAdapter();
adapter.output5V();
}
}
二、对象适配器(系统存在的角色转换成目标需要的内容)
场景说明:需要通过220V电压转换成5V电压,只需要5V电压,而不保留22V电压。
新增5V电压接口,新增适配器类构造方法入参为220V电压,实现DC5V接口,
在实现的output5V()方法里面实现220V转5V的逻辑。客户端调用适配器,只有DC5V的方法。
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输出电压" + output + "V");
return output;
}
}
/**
* 新增方法(目标类)
*/
public interface DC5 {
int output5V();
}
/**
* 对象适配器
* 符合最小知道原则,适配器只有AC的方法
*/
public class PowerAdapter implements DC5 {
private AC220 ac220;
public PowerAdapter(AC220 ac220) {
this.ac220 = ac220;
}
@Override
public int output5V() {
int adapterInput = ac220.outputAC220V();
int adapterOutput = adapterInput / 44;
System.out.println("使用Adapter输入AC" + adapterInput + "V,输出AC"+adapterOutput+"V");
return adapterOutput;
}
}
public class Test {
public static void main(String[] args) {
DC5 adapter = new PowerAdapter(new AC220());
adapter.output5V();
}
}
三、接口适配器
接口适配器
/**
* 原有类
*/
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输出电压" + output + "V");
return output;
}
}
public interface DC {
int output5V();
int output12V();
int output24V();
int output36();
}
/**
* 2、对象适配器
* 违背了接口隔离和单一职责原则,把所以功能聚集在一起减少类的关联关系
*/
public class PowerAdapter implements DC {
private AC220 ac220;
public PowerAdapter(AC220 ac220) {
this.ac220 = ac220;
}
@Override
public int output5V() {
int output = ac220.outputAC220V() / 44;
return output;
}
@Override
public int output12V() {
int output = ac220.outputAC220V() / 18;
return output;
}
@Override
public int output24V() {
int output = ac220.outputAC220V() / 9;
return output;
}
@Override
public int output36() {
int output = ac220.outputAC220V() / 6;
return output;
}
}
public class Test {
public static void main(String[] args) {
DC adapter = new PowerAdapter(new AC220());
adapter.output5V();
}
}
四、登录例子
背景:原项目只有注册,登录账号,现有的注册登录方法很稳定,现在需要增加第三方登录,底层还是调用旧的注册登录方法。
方式一
缺点:不容易再度扩展,此时如果再增加,微博登录...等等。需要改接口,改动适配器。不符合开闭原则。
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 PassportService {
/**
* 注册方法
* @param username
* @param password
* @return
*/
public ResultMsg regist(String username,String password) {
return new ResultMsg(200,"注册成功",new Member());
}
/**
* 登录方法
* @param username
* @param password
* @return
*/
public ResultMsg login(String username,String password) {
return new ResultMsg(200,"登录成功",new Member());
}
}
/**
* 目标接口
*/
public interface IPassportForOther {
ResultMsg loginForQQ(String openId);
ResultMsg loginForWechat(String openId);
ResultMsg loginForToken(String token);
ResultMsg loginForTelphone(String phone,String code);
}
/**
* 目标接口适配器
*/
public class PassportForOtherAdapter extends PassportService implements IPassportForOther {
@Override
public ResultMsg loginForQQ(String openId) {
// TODO 这里要跟腾讯SDK对接,代码逻辑很长,导致这个类很臃肿
return loginForRegist(openId,null);
}
@Override
public ResultMsg loginForWechat(String openId) {
return loginForRegist(openId,null);
}
@Override
public ResultMsg loginForToken(String token) {
return loginForRegist(token,null);
}
@Override
public ResultMsg loginForTelphone(String phone, String code) {
// TODO 这里要跟第三方短信平台对接,代码逻辑很长,导致这个类很臃肿
return loginForRegist(phone,null);
}
/**
* 第三方登录不可能用密码登录
* 所以约定第三方登录的时候设置一个默认的第三方通用THIRD_EMPTY,后台做一个标记如果是THIRD_EMPTY这个密码,则不走正常流程
* 如果有用户设置密码时不能设置这个密码,提示密码无效,请重新设置
* @param username
* @param password
* @return
*/
private ResultMsg loginForRegist(String username,String password) {
// 约定,密码为空,使用内置登录密码,并
if(null == password) {
password = "THIRD_EMPTY";
}
super.regist(username,password);
return super.login(username,password);
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
PassportForOtherAdapter adapter = new PassportForOtherAdapter();
adapter.login("admin","123456");
adapter.loginForQQ("sdfgsfghs");// QQ提供的唯一的开发ID
adapter.loginForWechat("falkjkjnkvf");// 微信提供的唯一的开放ID
}
}
进阶
方式一的实现方法不符合开闭原则。进阶版思路:用两个适配器,一个总适配器,起到调度的作用,每种登录方式都写一个适配器,增加登录方式,增加适配器类,不需要改动原有的代码,只需增加类。
/**
* 原有类
*/
public class PassportService {
/**
* 注册方法
* @param username
* @param password
* @return
*/
public ResultMsg regist(String username,String password) {
return new ResultMsg(200,"注册成功",new Member());
}
/**
* 登录方法
* @param username
* @param password
* @return
*/
public ResultMsg login(String username,String password) {
return new ResultMsg(200,"登录成功",new Member());
}
}
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 interface ILoginAdapter {
// 适配器是否匹配
boolean support(Object object);
// 调用第三方SDK获取登录的token
String token();
}
public interface IPassportForOther {
ResultMsg loginForOther();
}
public class LoginForQQAdapter implements ILoginAdapter{
@Override
public boolean support(Object object) {
return false;
}
@Override
public String token() {
System.out.println("发送http请求");
System.out.println("QQ登录,QQ返回的唯一ID->");
String username = "admin";
return username;
}
}
/**
* 对外提供的适配器
*/
public class PassportForOtherAdapter extends PassportService implements IPassportForOther{
private ILoginAdapter loginAdapter;
public PassportForOtherAdapter(ILoginAdapter loginAdapter) {
this.loginAdapter = loginAdapter;
}
/**
* 第三方登录
* @return
*/
@Override
public ResultMsg loginForOther() {
return loginForRegist(loginAdapter.token(),null);
}
/**
* 第三方登录不可能用密码登录,模拟注册之后再登录
* 所以约定第三方登录的时候设置一个默认的第三方通用THIRD_EMPTY,后台做一个标记如果是THIRD_EMPTY这个密码,则不走正常流程
* 如果有用户设置密码时不能设置这个密码,提示密码无效,请重新设置
*
* @param username
* @param password
* @return
*/
protected ResultMsg loginForRegist(String username, String password) {
if (null == password) {
password = "THIRD_EMPTY";
}
super.regist(username, password);
return super.login(username, password);
}
}
public class Test {
public static void main(String[] args) {
IPassportForOther adapter = new PassportForOtherAdapter(new LoginForQQAdapter());
adapter.loginForOther();
}
}