什么是适配器模式
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
简单的说适配器模式的核心就是用来解决接口不兼容的问题。例如现在你在新项目中开发了一个登录接口,接口定义如下:
public interface INewLoginService {
/**
* 登录
* @param userName 用户名
* @param password 密码
*/
void loginWithUserNameAndPassword(String userName, String password);
}
但是以前已经做过了登陆的功能,它的代码如下:
public interface IOldLoginService {
/**
* 登录
* @param userName 用户名
* @param password 密码
*/
void login(String userName,String password);
}
public class OldLoginServiceImpl implements IOldLoginService{
private static final String PASSWORD = "123456";
@Override
public void login(String userName, String password) {
if (PASSWORD.equals(password)){
System.out.println(String.format("用户[%s]登录成功",userName));
}else {
throw new RuntimeException("密码错误");
}
}
}
如果我们想复用之前登录相关代码,我们就得对相关代码做兼容,而适配器模式就是用来解决这个问题的。对于新老接口我们并不需要改动,我们只需要增加一个适配类就可以解决了。
public class OldLoginAdapter implements INewLoginService{
private IOldLoginService oldLoginService;
public OldLoginAdapter(IOldLoginService oldLoginService) {
this.oldLoginService = oldLoginService;
}
@Override
public void loginWithUserNameAndPassword(String userName, String password) {
oldLoginService.login(userName,password);
}
}
我们在这里创建一个OldLoginAdapter
适配器类,然后让这个类实现新的登录接口,而登录的实现是调用老的登录实现的,而这种模式就是适配器模式。下面就是我们客户端调用的相关代码:
public class App {
public static void main(String[] args) {
//对象适配
IOldLoginService oldLoginService = new OldLoginServiceImpl();
INewLoginService newLoginService = new OldLoginAdapter(oldLoginService);
newLoginService.loginWithUserNameAndPassword("mac","123456");
}
}
对象分析
一般在适配器模式中存在以下几种对象:
-
Target(目标抽象类):目标抽象类定义了客户端所需要的接口,一般是接口或者抽象类。在我们上面的例子中就是 INewLoginService
。 -
Adapter(适配器类):适配器可以调用另一个接口,它的作用就是对目标类(Target)做适配。在我们的例子中就是 OldLoginAdapter
。 -
Adaptee(适配者类):即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配新的接口。我们的例子中就是 IOldLoginService
及其实现。
对象适配和类适配
在我们上面的例子中,适配器类和适配者类的关系是关联,因为们在OldLoginAdapter
保留了一个IOldLoginService
实例的引用,通过这种方式实现的适配器模式我们一般称为对象适配
。其实我们还可以通过下面的方式来实现:
public class OldLoginAdapter2 extends OldLoginServiceImpl implements INewLoginService{
@Override
public void loginWithUserNameAndPassword(String userName, String password) {
login(userName,password);
}
}
上面代码我们通过继承OldLoginServiceImpl
并实现INewLoginService
来完成适配,这种方式我们称作类适配
。由于Java语言单继承的限制,这种适配方式可能无法适用,所以实际开发中我们更加推荐使用对象适配
这种方式。其实不难看出,类适配器模式和对象适配器模式最大的区别在于适配器和适配者之间的关系不同,对象适配器模式中适配器和适配者之间是关联关系,而类适配器模式中适配器和适配者是继承关系。
双向适配
所谓的双向适配是指既能实现A适配B,同时还能实现B适配A的情况。通常实现这种双向适配的方式是通过在适配器类中同时包含目标抽象类和适配者类,示例代码如下:
public class TwoWayAdapter implements IOldLoginService,INewLoginService{
private IOldLoginService oldLoginService;
private INewLoginService newLoginService;
public TwoWayAdapter(IOldLoginService oldLoginService) {
this.oldLoginService = oldLoginService;
}
public TwoWayAdapter(INewLoginService newLoginService) {
this.newLoginService = newLoginService;
}
@Override
public void loginWithUserNameAndPassword(String userName, String password) {
oldLoginService.login(userName, password);
}
@Override
public void login(String userName, String password) {
newLoginService.loginWithUserNameAndPassword(userName, password);
}
}
这种情况在实际的开发中会比较复杂,所以使用的场景也不是很多。
总结
对于适配器模式来说,它主要解决的就是兼容性问题。它的优点如下:
-
将目标类和适配者类解耦,因为我们并不需要修改目标类和适配者类。 -
增加了类的透明性和复用性,将复杂的业务实现封装在适配者类中,对于客户端而言是透明的,而且提高了适配者的的复用性。
它的缺点其实也是它的优点导致的,过多使用适配器会使得系统非常凌乱,明明调用的是A接口,内部却被适配成了B接口。所以在适用场景上来说,它更加适用于以下几种情况:
-
系统需要使用一些现有的类,而这些类的接口不符合系统的需求,甚至这些代码都无法修改(第三方依赖包)。 -
想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作。
本文示例代码地址:https://gitee.com/zengchao_workspace/design-pattern
原文始发于微信公众号(一只菜鸟程序员):设计模式(四)-适配器模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72894.html