社区精选|工作中常用的设计模式--适配器模式
今天小编为大家带来的是社区作者 lpe234 的文章,让我们一起来看看工作中常用的设计模式--适配器模式。
一般做业务开发,不太容易有大量使用设计模式的场景。这里总结一下在业务开发中使用较为频繁的设计模式。当然语言为Java,基于Spring框架。
1 适配器模式(Adapter Pattern)
已存在的接口、服务,跟我们所需、目的接口不兼容时,我们需要通过一定的方法将二者进行兼容适配。一个常见的例子,家用电源(国标)220V,而手机标准输入一般为5V,此时我们便需要一个适配器来将220V转换为5V使用。
适配器模式一般有3个角色:
Target: 目标接口
Adaptee: 需要进行适配的类(受改造者)
Adapter: 适配器(将Adaptee转为Target)
这个出现的场景其实挺多,但实际完全按照适配器模式编写代码的场景可能并不多。简单业务场景,直接就将适配、兼容代码混杂在业务代码中了。并没有将其摘出来处理。大部分业务代码,可能后续并不会再做扩展之类,过度设计反而会降低可读性并增加代码的复杂性。
适配器模式一般分为类适配器和对象适配器。
类适配器的话,使用继承方式实现:class Adapter extends Adaptee implements Target;而对象适配器的话,则使用组合方式实现。这种情况更灵活一些。毕竟大家都推荐多用组合少用继承。
在学生发生约课完课等事件时,我们需要将部分数据同步到外部CRM系统中。课程的话,按班级类型分为:1v1,小班课、大班课。不同的班级类型课程数据有所不同。事件上报时,并不是全量数据,有些数据需要消费者按需查询。如课程课程编号、名称、预约上课时间等。
不同班级类型的课程由三个不同的FeignClient(Adaptee)提供服务,而我们想要的就是查询课程相关信息(Target)。
为了模拟服务提供者,我们Mock如下服务。
@Data
@Builder
public class OneClass {
// 课程编号
private String lessonNo;
// 课程名称
private String lessonName;
// 其他信息
private String one;
}
@Data
@Builder
public class SmallClass {
// 课程编号
private String lessonNo;
// 课程名称
private String lessonName;
// 其他信息
private String small;
}
@Data
@Builder
public class BigClass {
// 课程编号
private String lessonNo;
// 课程名称
private String lessonName;
// 其他信息
private String big;
}
public interface RemoteClassClient {
default OneClass getOne() {
return OneClass.builder().lessonNo("one").lessonName("1V1").build();
}
default SmallClass getSmall() {
return SmallClass.builder().lessonNo("small").lessonName("小班课").build();
}
default BigClass getBig() {
return BigClass.builder().lessonNo("big").lessonName("大班课").build();
}
}
public class RemoteClassClientImpl implements RemoteClassClient {
}
该服务统一由RemoteClassClient对外提供各个班级类型的查询服务。
ClassService (Target目标接口、及目标对象)
/**
* 课程信息
*/
@Data
@Builder
public class ClassInfoBO {
// 课程类型 1:1v1 2:small 3:big
private String type;
// 班级ID
private String classId;
// 课程编号
private String lessonNo;
// 课程名称
private String lessonName;
}
/**
* 目标接口
*/
public interface ClassService {
boolean match(String classType);
ClassInfoBO getClassInfo(String classId);
}
下面我们就需要几个适配器来完成适配。
1.1 对象适配器
OneClassAdapter
/**
* 1v1适配器
*/
@Component
@RequiredArgsConstructor
public class OneClassAdapter implements ClassService {
private static final String TYPE = "1";
private final RemoteClassClient classClient;
@Override
public boolean match(String classType) {
return TYPE.equals(classType);
}
@Override
public ClassInfoBO getClassInfo(String classId) {
final OneClass one = classClient.getOne();
return ClassInfoBO.builder()
.type("1")
.classId(classId)
.lessonNo(one.getLessonNo()).lessonName(one.getLessonName())
.build();
}
}
SmallClassAdapter
/**
* 小班课适配器
*/
@Component
@RequiredArgsConstructor
public class SmallClassAdapter implements ClassService {
private static final String TYPE = "2";
private final RemoteClassClient classClient;
@Override
public boolean match(String classType) {
return TYPE.equals(classType);
}
@Override
public ClassInfoBO getClassInfo(String classId) {
final SmallClass small = classClient.getSmall();
return ClassInfoBO.builder()
.type("2")
.classId(classId)
.lessonNo(small.getLessonNo()).lessonName(small.getLessonName())
.build();
}
}
BigClassAdapter
/**
* 大班课适配器
*/
@Component
@RequiredArgsConstructor
public class BigClassAdapter implements ClassService {
private static final String TYPE = "3";
private final RemoteClassClient classClient;
@Override
public boolean match(String classType) {
return TYPE.equals(classType);
}
@Override
public ClassInfoBO getClassInfo(String classId) {
final BigClass big = classClient.getBig();
return ClassInfoBO.builder()
.type("3")
.classId(classId)
.lessonNo(big.getLessonNo()).lessonName(big.getLessonName())
.build();
}
}
至此,适配器完成。可以根据具体场景选择不同的适配器,去适配当前的场景。再来个适配器工厂类。
ClassAdapterFactory
/**
* 课程信息适配器工厂
*/
@Service
@RequiredArgsConstructor
public class ClassAdapterFactory {
private final List<ClassService> classServiceList;
ClassService getAdapter(String classType) {
return classServiceList.stream()
.filter(cs -> cs.match(classType)).findFirst()
.orElse(null);
}
}
1.2 类适配器
仅以其一举例。这种适配器不如对象适配器灵活。
/**
* 小班课适配器(类适配器)
*/
@Component
public class SmallClassAdapter2 extends RemoteClassClientImpl implements ClassService {
private static final String TYPE = "2";
@Override
public boolean match(String classType) {
return TYPE.equals(classType);
}
@Override
public ClassInfoBO getClassInfo(String classId) {
final SmallClass small = super.getSmall();
return ClassInfoBO.builder()
.type("2")
.classId(classId)
.lessonNo(small.getLessonNo()).lessonName(small.getLessonName())
.build();
}
}
可以看到,我们通过继承的方式,使SmallClassAdapter2具备了RemoteClassClientImpl查询课程信息的能力。跟SmallClassAdapter也没有啥太大区别。
1.3 单测
@SpringBootTest
class ClassServiceTest {
@Autowired
private ClassAdapterFactory adapterFactory;
@Test
void testOne() {
String classType = "1";
String classId = "11111111";
Optional.ofNullable(adapterFactory.getAdapter(classType)).ifPresent(ad -> {
final ClassInfoBO classInfo = ad.getClassInfo(classId);
assertEquals("one", classInfo.getLessonNo());
});
}
@Test
void testSmall() {
String classType = "2";
String classId = "22222222";
Optional.ofNullable(adapterFactory.getAdapter(classType)).ifPresent(ad -> {
final ClassInfoBO classInfo = ad.getClassInfo(classId);
assertEquals("small", classInfo.getLessonNo());
});
}
@Test
void testBig() {
String classType = "3";
String classId = "33333333";
Optional.ofNullable(adapterFactory.getAdapter(classType)).ifPresent(ad -> {
final ClassInfoBO classInfo = ad.getClassInfo(classId);
assertEquals("big", classInfo.getLessonNo());
});
}
}
2 思考
适配器模式能带来什么?
感觉最主要的还是带了一种解决方案( 关注公众号:拾黑(shiheibook)了解更多
赞助链接:
关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 韩元汇率对人民币2024年2月28日
- PhotoEditor免费好用的AI快速移除图片中的物品工具
- 又一“普通人”火了!抖音这回给微信上了一课
- 美元兑人民币汇率2023年9月14日
- MINISTERAI集成了多种人工智能工具的网站
- 掌机与分体VR结合,小派Pimax Portal浅析
- Python与JavaScript做比较公平吗?
- 最难618:商家“躺平”,活下去更重要
- 被 Google Play “判定”违反政策:开源软件 FairEmail 开发者一怒之下关停所有项目
- 关于苹果造车,看这一篇文章就够了
- 一机如何多用 ? 用 Ta 解决当'坞'之急 !
- iPhone 12 Pro Max 动手玩:生产力野心“更大”了?