个别做业务开发,不太容易有大量应用设计模式的场景。这里总结一下在业务开发中应用较为频繁的设计模式。当然语言为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@Builderpublic class OneClass {    // 课程编号    private String lessonNo;    // 课程名称    private String lessonName;    // 其余信息    private String one;}@Data@Builderpublic class SmallClass {    // 课程编号    private String lessonNo;    // 课程名称    private String lessonName;    // 其余信息    private String small;}@Data@Builderpublic 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@Builderpublic 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@RequiredArgsConstructorpublic 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@RequiredArgsConstructorpublic 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@RequiredArgsConstructorpublic 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@RequiredArgsConstructorpublic class ClassAdapterFactory {    private final List<ClassService> classServiceList;    ClassService getAdapter(String classType) {        return classServiceList.stream()                .filter(cs -> cs.match(classType)).findFirst()                .orElse(null);    }}

1.2 类适配器

仅以其一举例。这种适配器不如对象适配器灵便。

/** * 小班课适配器(类适配器) */@Componentpublic 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 单测

@SpringBootTestclass 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 思考

适配器模式能带来什么?

  1. 感觉最次要的还是带了一种解决方案(当然设计模式原本就是如此)。其次是对某些场景提供了一种标准,退出没有这个设计模式,咱们也能有解决方案,但具体实现可能千奇百怪(当然设计模式原本就是从千奇百怪中提炼进去的)。
  2. 设计模式不是银弹。防止滥用。

跟策略模式有啥区别?

  1. 单从实现形式(套路)来说。不能说毫无区别,几乎就是截然不同。
  2. 细想一下,Adaptee在策略模式中,并不是必然存在的。它的目标是不同策略的具体实现和调用离开。而适配器模式的重点在于Adaptee -> Target的适配。

封面图起源: https://refactoring.guru/desi...