目录
- 一、适配器模式是什么?
- 二、示例【[来源](https://refactoringguru.cn/design-patterns/adapter)】
- 1 第三方依赖 (接口A + 数据A)
- 2 客户端
- 3 方钉转圆钉的适配器(数据B)
- 三、适配器(xxxAdapter)怎么写?
一、适配器模式是什么?
- 适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。
- 背景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()方法。*/@Overridepublic 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;@Overridepublic double getRadius() {return squarePeg.getWidth() * Math.sqrt(2) / 2;}
}