目录
一、适配器模式是什么?
- 适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。
- 背景1:
- 客户端通过接口A处理数据A,但是用户可能传入数据B。
- 一种解决方案是:为数据B实现接口B。但这样做的话,需要修改客户端的代码(尽量不修改上层代码)。而且接口B不一定好实现。
- 另一种解决方案是:提供适配器,让数据B去适配接口A。
- 背景2:
- 客户端传入数据A,然而,系统中只有处理数据B的接口B。
- 因此,需要提供适配器,让数据A去适配接口B。
- 类比:生活中,iphone用的是Lightning接口,而很多充电器已经是type-c了。这就需要Lightning转type-c的适配器。
二、示例【来源】
- 需求:判断圆洞能否匹配圆钉(假设这是第三方依赖提供的能力)
- 但是用户还会传入的是方钉(这怎么复用呢?)
1 第三方依赖 (接口A + 数据A)
接口A:RoundHole::fits
数据A:RoundPeg
@Data
@AllArgsConstructor
public class RoundHole {
private double radius;
public boolean fits(RoundPeg peg) {
return this.radius >= peg.getRadius();
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RoundPeg {
private double radius;
}
2 客户端
public class Application {
public static void main(String[] args) {
// 判断圆洞能否匹配圆钉(假设这是第三方依赖提供的能力)
RoundHole roundHole = new RoundHole(2D);
List<RoundPeg> roundPegs = ImmutableList.of(
new RoundPeg(1D),
new RoundPeg(3D)
);
roundPegs.stream().forEach(roundPeg -> {
if (roundHole.fits(roundPeg)) {
System.out.println(String.format("半径为%.2f的圆钉可以插入半径为%.2f的圆洞种", roundPeg.getRadius(), roundHole.getRadius()));
} else {
System.out.println(String.format("半径为%.2f的圆钉不能插入半径为%.2f的圆洞种", roundPeg.getRadius(), roundHole.getRadius()));
}
});
}
}
3 方钉转圆钉的适配器(数据B)
@Data
@AllArgsConstructor
public class SquarePeg {
private double width;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SquarePegAdapter extends RoundPeg {
private SquarePeg squarePeg;
/**
* public boolean fits(RoundPeg peg) {
* return this.radius >= peg.getRadius();
* }
*
* 从客户端使用方式可知,SquarePegAdapter需要继承RoundPeg,并且覆写getRadius()方法。
*/
@Override
public double getRadius() {
return squarePeg.getWidth() * Math.sqrt(2) / 2;
}
}
- 客户端代码:
public class Application {
public static void main(String[] args) {
// 判断圆洞能否匹配圆钉(假设这是第三方依赖提供的能力)
RoundHole roundHole = new RoundHole(2D);
List<RoundPeg> roundPegs = ImmutableList.of(
new RoundPeg(1D),
new RoundPeg(3D)
);
roundPegs.stream().forEach(roundPeg -> {
if (roundHole.fits(roundPeg)) {
System.out.println(String.format("半径为%.2f的圆钉可以插入半径为%.2f的圆洞种", roundPeg.getRadius(), roundHole.getRadius()));
} else {
System.out.println(String.format("半径为%.2f的圆钉不能插入半径为%.2f的圆洞种", roundPeg.getRadius(), roundHole.getRadius()));
}
});
// 但是用户还会传入的是方钉(这怎么复用呢?)
List<SquarePeg> squarePegs = ImmutableList.of(
new SquarePeg(1D),
new SquarePeg(3D)
);
SquarePegAdapter squarePegAdapter = new SquarePegAdapter();
squarePegs.stream().forEach(squarePeg -> {
squarePegAdapter.setSquarePeg(squarePeg);
if (roundHole.fits(squarePegAdapter)) {
System.out.println(String.format("半径为%.2f的方钉可以插入半径为%.2f的圆洞种", squarePeg.getWidth(), roundHole.getRadius()));
} else {
System.out.println(String.format("半径为%.2f的方钉不能插入半径为%.2f的圆洞种", squarePeg.getWidth(), roundHole.getRadius()));
}
});
}
}
/**
半径为1.00的圆钉可以插入半径为2.00的圆洞中
半径为3.00的圆钉不能插入半径为2.00的圆洞中
半径为1.00的方钉可以插入半径为2.00的圆洞中
半径为3.00的方钉不能插入半径为2.00的圆洞中
*/
三、适配器(xxxAdapter)怎么写?
- 思路:
- (1)从客户端代码入手,确定什么是不变的。
public boolean fits(RoundPeg peg) {
return this.radius >= peg.getRadius();
}
如果fits()方法不变,那么就得将SquarePeg适配成RoundPeg
- (2)确定被适配对象
- SquarePeg,方法取名为SquareAdapter
- (3)实现SquareAdapter
public class SquarePegAdapter extends RoundPeg {
private SquarePeg squarePeg;
@Override
public double getRadius() {
return squarePeg.getWidth() * Math.sqrt(2) / 2;
}
}