介绍
什么是适配器模式?从“适配”这个词上理解,就是把原来两个不能一起工作的东西可以一起工作。而适配器模式就是使得原来由于接口不兼容而不能一起工作的那些类可以一起工作。适配器模式是一种结构型模式。
通俗解释:原本目标角色接口和源角色类,一个接口,一个类,两者并无关系,我们不能去修改源角色类去实现目标角色接口,这样不符合开闭原则。那么为了让二者兼容,就设计一个适配器类,让适配器成为源角色类和目标角色接口的桥梁。而我们调用源角色类的方法,可以使用继承,这样就是类适配器。如果我们使用组合,这样就是对象适配器。
源(Adapee)角色
目标(Target)角色
适配器(Adapter)角色
类适配器
- 源角色
public class MySourceRole {
public void method1() {
System.out.println("method1");
}
}
- 目标接口
public interface ITarget {
public void method1();
public void method2();
}
- 适配器角色
public class MyAdapter extends MySourceRole implements ITarget {
@Override
public void method1() {
super.method1();
}
@Override
public void method2() {
System.out.println("method2");
}
}
测试:
public static void main(String[] args) {
ITarget target = new MyAdapter();
target.method1();
target.method2();
}
说明:目标接口通过适配器继承源角色获取了method1功能,适配器本身实现了method2功能,并且又兼容了method1功能。可以看出类适配的原理就是为目标接口创建适配器,让适配器实现目标接口,继承源角色。
对象适配器
- 源角色
public class MySourceRole {
public void method1() {
System.out.println("method1");
}
}
- 目标接口
public interface ITarget {
public void method1();
public void method2();
}
- 适配器角色
public class MyAdapter implements ITarget {
private MySourceRole mySourceRole;
public MyAdapter(MySourceRole mySourceRole) {
this.mySourceRole = mySourceRole;
}
@Override
public void method2() {
System.out.println("method2");
}
@Override
public void method1() {
mySourceRole.method1();
}
}
测试:
public static void main(String[] args) {
ITarget target = new MyAdapter(new MySourceRole());
target.method1();
target.method2();
}
说明:适配器是通过构造器方式传入源角色,进而使用源角色的引用实现了method1的功能;其本身实现了method2的功能且兼容了method1的功能。可以发现method1的功能是适配器在出生(初始化)时就赋予的能力!
类适配器 vs 对象适配器
区别:类适配器在兼容源角色的策略采用继承方式,通过继承父类从而实现新功能。对象适配器在兼容源角色的策略采用组合+构造器初始化方式,通过传入源角色的引用从而实现新功能。
思考1,项目中什么情况下使用适配器模式?
- 当我们想要调用别人提供的jar包中的实现类,发现我们定义的接口,二者毫无关联、不兼容。
- 别人提供了新增订单的接口,但是我们定义的是商品服务接口,为了让二者适配,那么我们设计一个商品适配器类,让二者兼容。
public interface IGoodsService {
/**
* 下单商品
* @return
*/
public int placeGoods(String goodsId);
/**
* 修改商品属性
* @param goodsId
* @return
*/
public int modifyGoods(String goodsId);
}
@Slf4j
@Service("orderService")
public class OrderServiceImpl {
/**
* 根据goodsId为其新增一笔订单
* @param goodsId
*/
public void addOrder(String goodsId) {
log.info("根据goodsId新增一笔订单");
}
}
@Slf4j
@Service("goodsService")
public class GoodsAdapter extends OrderServiceImpl implements IGoodsService {
/**
* 为指定的商品下订单
* @param goodsId
* @return
*/
@Override
public int placeGoods(String goodsId) {
super.addOrder(goodsId);
return 1;
}
/**
* 修改商品的属性
* @param goodsId
* @return
*/
@Override
public int modifyGoods(String goodsId) {
log.info("根据goodsId修改商品属性");
return 1;
}
}
思考2,可以将思考1中的代码改造为对象适配器
@Slf4j
@Service
public class GoodsAdapter implements IGoodsService {
@Autowired
private OrderServiceImpl orderService;
/**
* 为指定的商品下订单
* @param goodsId
* @return
*/
@Override
public int placeGoods(String goodsId) {
orderService.addOrder(goodsId);
return 1;
}
/**
* 修改商品的属性
* @param goodsId
* @return
*/
@Override
public int modifyGoods(String goodsId) {
log.info("根据goodsId修改商品属性");
return 1;
}
}
IO流中适配器的使用
适配器在转换流InputStreamReader的应用:
public static void main(String[] args) throws IOException {
Reader reader = new InputStreamReader(System.in);
char[] arr = new char[1024];
int len = 0;
while ((len = reader.read(arr)) != -1) {
System.out.println(new String(arr, 0 , len));
}
reader.close();
}
① 目标类Reader ② 源角色InputStream ③ 适配器InputStreamReader
InputStreamReader适配器将InputStream输入流转换为Reader字符流。InputStream和Reader提供都是正确的,但是与目标接口Reader不符合,就需要使用适配器InputStreamReader,让InputStream适配Reader。
总结
- 类的适配器:将一个类(原类)转成满足另一个接口(目标接口)的新类(适配器类),就创建一个适配器类实现目标接口,继承原类。
- 对象的适配器:将一个对象(原对象)转成满足新接口(目标接口)的新对象(适配器对象),就使新对象持有原对象,将原对象作为属性,通过原对象调用原对象的方法。