设计模式之七适配器模式

概念

适配器模式(Adapater Pattern)是指将一个类的接口转换成用户期望的另一个接口,使原本接口不兼容的类可以一起工作,属于结构型设计模式。

场景

场景一

已经存在的类的方法和需求不匹配(方法结果相同或者相似)的情况

场景二

适配器模式不是软件设计初始阶段考虑的设计模式,是随着软件的发展,由于不同的产品、不同的厂家造成功能类似而接口不同的问题的解决方法,有点亡羊补牢的感觉。

生活中电源插座转接口,电脑视频输出的转接头,手机充电转接头,笔记本电源(适配器)

案例一

国内民用电都是220V交流电,但手机锂电池使用的是5V直流电。因此,我们给手机充电时需要使用电源适配器来进行转换。下面用代码实现这个生活场景:

package com.hanker.mythread;
//模拟220v电源
class AC220{
	public int outputAC220V() {
		int output = 220;
		System.out.println("输出交流电:"+output+"v");
		return output;
	}
}
//模拟手机充电器
interface DC5{
	int outputDC5V();
}
//电源适配器类
class PowerAdapter implements DC5{
	private AC220 ac220; //这是原接口
	public PowerAdapter(AC220 ac220) {
		this.ac220 = ac220;
	}
	@Override
	public int outputDC5V() {
		int adapaterInput = ac220.outputAC220V();//220V
		//转换..变压器
		int adapterOutput = adapaterInput / 44;
		System.out.println("使用PowerAdapter输入AC:"+adapaterInput+"v,输出DC:"+adapterOutput+"v");
		return adapterOutput;
	}
	
}

public class AdapterPatternDemo1 {

	public static void main(String[] args) {
		AC220 ac220 = new AC220();
		DC5 dc5 = new PowerAdapter(ac220);
		int dc52= dc5.outputDC5V();
		System.out.println("适配器转换后的电压:"+dc52+"v");
		
	}

}

案例二

老系统开发都有登录接口,但是随着业务的发展,单纯地依赖用户名和密码登录不能满足用户的需求了。现在大部分系统都已经支持多种登录方式如: QQ登录、微信登录、手机号登录、微博登录等,同时保留用户名和密码登录的方式。虽然登录形式丰富了,但是登录后台的处理逻辑可以不改,同样是将登录状态保存到session,遵循开闭原则,下面是代码实现。

创建统一的返回结果类 ResultMsg:

package com.hanker.mythread;

public class ResultMsg {

	private int code;
	private String msg;
	private Object data;
	public ResultMsg(int code, String msg, Object data) {
		super();
		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;
	}	
}

假设老系统的登录代码如下:

package com.hanker.mythread;

public class SiginService {

	/**
	 * 注册丰富
	 * @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 null;
	}
}

为了遵循开闭原则,我们不修改老系统的代码。下面开始重构代码,先创建Member类:

package com.hanker.mythread;

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

再创建新的类继承原来的代码:

package com.hanker.mythread;
//稳定的方法直接继承下来
public class SiginForThirdService extends SiginService {

	public ResultMsg loginForQQ(String openId) {
		//1.openId 是全局唯一的,我们可以把它当做一个用户名
		//2.密码默认为QQ_EMPTY
		//3.注册(在原系统里创建一个用户)
		//4.调用原来的登录方法
		return loginForRegist(openId,null);
	}
	
	public ResultMsg loginForWechat(String token) {
		//通过token获取用户信息,然后重新登录一次
		return null;
	}
	
	public ResultMsg loginForTelphone(String telphone,String code) {
		return null;
	}
	
	public ResultMsg loginForRegist(String username,String password) {
		super.regist(username, password);
		return super.login(username, password);
	}
}

客户端测试代码

package com.hanker.mythread;
public class AdapaterPatternDemo2 {
	public static void main(String[] args) {
		SiginForThirdService service = new SiginForThirdService();
		//不改变原来代码,也能够兼容新的需求,还可以再加一层策略模式
		ResultMsg resultMsg = service.loginForQQ("1212esadfasdfasdfas");
		System.out.println(resultMsg);
	}
}

通过一个简单的过程,就完成了代码的兼容,当然,我们的代码还可以更加优雅,根据不同的登录方式创建不同的“Adapter”。

首先创建LoginAdapter接口:

package com.hanker.mythread.adaper2;
import com.hanker.mythread.ResultMsg;
public interface LoginAdapter {
	boolean support(Object adapter);
	ResultMsg login(String id,Object adapter);
}

然后,分别实现不同的登录方式,QQ登录LoginForQQAdapter如下:

package com.hanker.mythread.adaper2;
import com.hanker.mythread.ResultMsg;
public class LoginForQQAdapter implements LoginAdapter{
	@Override
	public boolean support(Object adapter) {
		return adapter instanceof LoginForQQAdapter;
	}
	@Override
	public ResultMsg login(String id, Object adapter) {
		return null;
	}
}

新浪微博登录LoginForSinaAdapter如下:

package com.hanker.mythread.adaper2;
import com.hanker.mythread.ResultMsg;
public class LoginForSinaAdapter implements LoginAdapter {
	@Override
	public boolean support(Object adapter) {
		return adapter instanceof LoginForSinaAdapter;
	}
	@Override
	public ResultMsg login(String id, Object adapter) {
		return null;
	}
}

手机号登录 LoginForTelAdapter如下:

package com.hanker.mythread.adaper2;

import com.hanker.mythread.ResultMsg;

public class LoginForTelAdapter implements LoginAdapter {

	@Override
	public boolean support(Object adapter) {
		return adapter instanceof LoginForTelAdapter;
	}

	@Override
	public ResultMsg login(String id, Object adapter) {
		return null;
	}

}

Token自动登录LoginForTokenAdapter如下

package com.hanker.mythread.adaper2;

import com.hanker.mythread.ResultMsg;

public class LoginForTokenAdapter implements LoginAdapter{

	@Override
	public boolean support(Object adapter) {
		return adapter instanceof LoginForTokenAdapter;
	}

	@Override
	public ResultMsg login(String id, Object adapter) {
		return null;
	}
}

微信登录 LoginForWechatAdapter 如下:

package com.hanker.mythread.adaper2;
import com.hanker.mythread.ResultMsg;
public class LoginForWechatAdapter implements LoginAdapter{
	@Override
	public boolean support(Object adapter) {
		return adapter instanceof LoginForWechatAdapter;
	}
	@Override
	public ResultMsg login(String id, Object adapter) {
		return null;
	}
}

接着创建第三方登录兼容接口IPassportForThird:

package com.hanker.mythread.adaper2;
import com.hanker.mythread.ResultMsg;
public interface IPassportForThird {

	/**
	 * QQ登录
	 * @param id
	 * @return
	 */
	ResultMsg loginForQQ(String id);
	/**
	 * 微信登录
	 * @param id
	 * @return
	 */
	ResultMsg loginForWechat(String id);
	/**
	 * 手机号登录
	 * @param id
	 * @return
	 */
	ResultMsg loginForTelphone(String id);
	/**
	 * 记住登录状态后自动登录
	 * @param id
	 * @return
	 */
	ResultMsg loginForToken(String id);
	/**
	 * 注册后自动登录
	 * @param id
	 * @return
	 */
	ResultMsg loginForRegist(String id);
}

实现兼容PassportForThirdAdapter:

package com.hanker.mythread.adaper2;

import com.hanker.mythread.ResultMsg;
import com.hanker.mythread.SiginService;
//第三方登录自由适配
public class PassportForThirdAdapter extends SiginService implements IPassportForThird {

	@Override
	public ResultMsg loginForQQ(String id) {
		return processLogin(id, LoginForQQAdapter.class);
	}
	@Override
	public ResultMsg loginForWechat(String id) {
		return processLogin(id, LoginForWechatAdapter.class);
	}

	@Override
	public ResultMsg loginForTelphone(String id) {
		return processLogin(id, LoginForTelAdapter.class);
	}

	@Override
	public ResultMsg loginForToken(String token) {
		return processLogin(token, LoginForTokenAdapter.class);
	}
	public ResultMsg loginForRegist(String username,String passport) {
		super.regist(username, null);
		return super.login(username, null);
	}

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

客户端测试代码如下:

package com.hanker.mythread.adaper2;
import com.hanker.mythread.ResultMsg;
public class ClientTest {
	public static void main(String[] args) {
		IPassportForThird passportForThird = new PassportForThirdAdapter();
		ResultMsg resultMsg = passportForThird.loginForQQ("xxxx");
		System.out.println(resultMsg);
	}
}

最后我们看一下类图:
在这里插入图片描述
在这里插入图片描述

适配器模式在spring中的体现

Spring中适配器模式也应用非常广泛,例如SpringAOP中的AdvisorAdapter类,它有三个实现类: MethodBeforeAdviceAdapter、AfterReturnningAdvisorAdapter、和ThrowsAdvisorAdapter。先看看顶层接口AdvisorAdapter的源代码:

public interface AdvisorAdapter {
	boolean supportsAdvice(Advice advice);
	MethodInterceptor getInterceptor(Advisor advisor);
}

再看MethodBeforeAdviceAdapter类:

@SuppressWarnings("serial")
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}
	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}
}

SpringMVC中的HandlerAdapter类,也有多个子类。
在这里插入图片描述

优缺点

优点

①能提高类的透明性和复用性,现有的类会被复用但不需要改变
②目标类和适配器类解耦,可以提高程序的扩展性
③在很多业务场景中符合开闭原则

缺点

①在适配器代码编写过程中需要进行全面考虑,可能会增加系统的复杂性
②增加了代码的阅读难度,降低了代码的可读性,过多使用适配器会使系统的代码变得凌乱

发布了91 篇原创文章 · 获赞 43 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/kongfanyu/article/details/105016779