适配器模式(Adapter Pattern)
指将一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作。
属于结构型设计模式。
适用场景:
1.已经存在的类,它的方法和需求不匹配的情况。
2.适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品,不同厂家造成功能类似而接口不相同情况下的解决方案。
代码场景:
现在需要给系统加上第三方登陆,要在保留原登陆接口不变的情况下加上。
/**
* @Author Darker
* @Descrption
* @Date : Created in 16:07 2020-3-12
*/
@Data
@AllArgsConstructor
public class MsgResult {
private int code;
private Object data;
private String msg;
public static MsgResult success(Object data){
return new MsgResult(200,data,"成功");
}
public static MsgResult fail(Object data){
return new MsgResult(404,data,"失败");
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 相当于系统已经写好的代码,不去动它
* @E-Mail : [email protected]
* @Date : Created in 13:38 2020-3-14
*/
public class LoginService {
//用户名,密码登陆方法
public MsgResult login(String username,String password){
return null;
}
//注册方法
public MsgResult register(String username,String password){
return login(username,password);
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 新的登陆接口
* @E-Mail : [email protected]
* @Date : Created in 14:02 2020-3-14
*/
public interface NewLogin {
public MsgResult loginQQ(String id);
public MsgResult loginWechat(String id);
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 适配器方式
* @E-Mail : [email protected]
* @Date : Created in 13:54 2020-3-14
*/
public class LoginForThird extends LoginService implements NewLogin{
@Override
public MsgResult loginQQ(String id) {
System.out.println("通过qq登陆");
return null;
}
@Override
public MsgResult loginWechat(String id) {
System.out.println("通过微信登陆");
return null;
}
}
这就是一个最简单的适配器模式,当然也很像静态代理模式,因为设计模式是一种思想而不是局限于代码,只是用代码的方式来实现这种思想,当然,这样肯定是太简单了,那么我们现在来实现下业内比较流行的增加第三方登陆的方式。
第一步:我们保留原系统login不变
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 相当于系统已经写好的代码,不去动它
* @E-Mail : [email protected]
* @Date : Created in 13:38 2020-3-14
*/
public class LoginService {
//用户名,密码登陆方法
public MsgResult login(String username,String password){
System.out.println("用户pc登陆");
return null;
}
//注册方法
public MsgResult register(String username,String password){
return login(username,password);
}
}
第二步:我们加上一个适配器接口,和所有的适配器
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption 在适配器中,这个接口可有可无,不要和模板模式混淆
* 模板模式一定是抽象类,这里只是一个接口
* @E-Mail : [email protected]
* @Date : Created in 14:38 2020-3-14
*/
public interface LoginAdapter {
//判断是否是需要调用的适配器,是否兼容
/**
* 为什么要这个方法,因为适配器可能会有多个,比如再来个注册适配器
* @param adapter
* @return
*/
boolean support(Object adapter);
public MsgResult login(String id,Object adapter);
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 14:41 2020-3-14
*/
public class LoginQQAdapter implements LoginAdapter{
@Override
public boolean support(Object adapter) {
return adapter instanceof LoginQQAdapter;
}
@Override
public MsgResult login(String id, Object adapter) {
//写自己qq登陆的逻辑
System.out.println("通过qq登陆");
return null;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 14:42 2020-3-14
*/
public class LoginWechatAdapter implements LoginAdapter {
@Override
public boolean support(Object adapter) {
return adapter instanceof LoginWechatAdapter;
}
@Override
public MsgResult login(String id, Object adapter) {
//写微信自己登陆的逻辑
System.out.println("通过微信登陆");
return null;
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 13:54 2020-3-14
*/
public class LoginForThird extends LoginService implements NewLogin{
//这里可以继续使用策略模式来优化
@Override
public MsgResult loginQQ(String id) {
return loginAdapterFactory(id,LoginQQAdapter.class);
}
@Override
public MsgResult loginWechat(String id) {
return loginAdapterFactory(id,LoginWechatAdapter.class);
}
//简单工厂
public MsgResult loginAdapterFactory(String id,Class <? extends LoginAdapter> clazz){
try {
LoginAdapter adapter = clazz.newInstance();
if(adapter.support(adapter)){
return adapter.login(id,adapter);
}
} catch (Exception e) {
e.printStackTrace();
}
return MsgResult.fail(null);
}
}
/**
* @Author Darker
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 17:55 2020-3-13
*/
public class AdapterTest {
public static void main(String[] args) {
LoginForThird loginForThird = new LoginForThird();
loginForThird.login("xiaoming","123456");
loginForThird.loginQQ("dsadas");
loginForThird.loginWechat("1556");
}
}
这样符合开闭原则,又用适配器解决了其它第三方登陆的需求,同时如果需要其它的登陆,也只需要继续增加适配器就可以了。
看一下类图:
我们来看看spring-aop中用到的策略模式:
springAop中的消息通知中就使用了这个适配器模式 ,选择你要的适配器,然后看是在方法前通知还是方法后通知。
再来看看spring-mvc中的适配器:
这里的适配器主要是在servlet里面使用 ,有兴趣的小伙伴可以去DispatcherServlet中的doDispatch中看看。
总结:
优点:
1.能提高类的透明性和复用,现有的类的复用不需要去做改变。
2.目标类和适配器类的解耦。
3.很多场景中符合开闭原则。
缺点:
1.需要全面考虑,而且很可能增加系统的复杂性。
2.增加阅读代码的难度,过多使用可能使代码变的很乱。