工作中常用到的设计模式-短信发送策略

一、概述

我们工作的业务代码从注释、命名、方法和异常等多方面实现整洁代码,但是感觉不够简洁,不够优雅,因为这是设计上的不足,总结就是抽象不够、可读性低、不够健壮。那这些就用到了设计模式。

java本身没有设计模式,但是随着时代的发展,写代码是人多了,他们便总结出了一套能提高开发和维护效率的套路,这就是设计模式。设计模式不是什么教条或者范式,它可以说是一种在特定场景下普适且可复用的解决方案,是一种可以用于提高代码可读性、可扩展性、可维护性的最佳实践。

二、短信发送策略

我目前在职的公司主营业务是智能语音外呼,主要是用机器人语音外呼替代人工,为了提升外呼之后的转换,通常会给客户发送一条挂机短信。但是因为通信通道的不稳定性以及业务复杂性(每个通道能发送的短信模板都是不一样的,比如营销类短信,游戏类短信),导致我们经常要对接各种短信通道。多数的小伙伴就会写出以下的代码:

if(type=="A通道"){
   //按照A通道的对接文档进行对接
 
}else if(type=="B通道"){
   //按照B通道的对接文档进行对接
}else{
    //按照默认格式解析
}

这个代码可能会存在哪些问题呢?

  • 如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低。
  • 如果你需要接入一种新的解析类型,那只能在原有代码上修改。

说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则、单一原则以及迪米特法则

  • 开闭原则(对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码
  • 单一原则(规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。
  • 迪米特法则(最少知道原则):发送短信逻辑和各个下游接口高度耦合,这导致接口的改变将直接影响到代码的组织,使得代码的可维护性降低。

如果你的代码就是:有多个if…else等条件分支,并且每个条件分支,可以封装起来替换的,这个案例就可以使用策略模式和适配器模式来优化。

首先是策略模式:策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。

策略模式通常包含以下角色:

  • 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
  • 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

然后是适配器模式:适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

适配器模式包含以下主要角色:

  • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  • 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  • 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
// 策略接口
interface Strategy {
    void send(Object ... params);
}

//适配者接口
class Adaptee
{
    public void specificRequest()
    {       
        System.out.println("适配者中的业务代码被调用!");
    }
}

// 联通策略+对象适配器类
class CUCC implements Strategy {
   private CUCCAdapteeService cuccService;
    @Override
    public void send(Object... params) {
        CUCCRequest request = new CUCCRequest();
        // 构建入参
        request.setCUCCReq(params);
        cuccService.specificRequest(request);
    }
}

// 移动策略+对象适配器类
class CMCC implements Strategy {
   private CMCCAdapteeService cmccService;
    @Override
    public void send(Object... params) {
        CMCCRequest request = new CMCCRequest ();
        // 构建入参
        request.setCMCCReq(params);
        cmccService.specificRequest(request);
    }
}

// 电信策略+对象适配器类
class CTCC implements Strategy {
   private CTCCAdapteeService ctccService;
    @Override
    public void send(Object... params) {
        CTCCRequest request = new CTCCRequest ();
        // 构建入参
        request.setCTCCReq(params);
        ctccService.specificRequest(request);
    }
}

CUCCAdapteeService 、CMCCAdapteeService 、CTCCAdapteeService 都是对象适配器类

代码经过优化后,虽然结构和设计上比之前要复杂不少,但考虑到健壮性和拓展性,还是非常值得的。

// 使用分支判断获取的策略上下文
class StrategyContext {
    public static Strategy getStrategy(String sendType) {
        switch (sendType) {
            case "CMCC":
                return new CMCC();
            case "CUCC":
                return new CUCC();
            case "CTCC":
                return new CTCC();
            default:
                throw new IllegalArgumentException("sendType error!");
        }
    }
}
// 优化后的策略服务
class SendService {
    public void send(String sendType , Object ... params) {
        Strategy strategy = StrategyContext.getStrategy(sendType);
        strategy.send(params);
    }
}

其实这就是工厂模式,定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。工厂模式一般配合策略模式一起使用。

但是这里发现策略类是无状态的模型,那就可以将策略类单例化以减少开销,并实现自注册的功能彻底解决分支判断。

所以在策略环境类StrategyContext 中使用一个注册表来记录各个策略类的注册信息,并提供接口供策略类调用进行注册。同时使用饿汉式单例模式去优化策略类的设计:

// 策略上下文,用于管理策略的注册和获取
class StrategyContext {
    private static final Map<String, Strategy> registerMap = new HashMap<>();
    // 注册策略
    public static void registerStrategy(String sendType, Strategy strategy) {
        registerMap.putIfAbsent(sendType, strategy);
    }
    // 获取策略
    public static Strategy getStrategy(String sendType) {
        return registerMap.get(sendType);
    }
}
// 抽象策略类
abstract class AbstractStrategy  {
    // 类注册方法
    public void register() {
        StrategyContext.registerStrategy(getClass().getSimpleName(), this);
    }
}
// 饿汉式单例CMCC策略
class CMCC extends AbstractStrategy implements Strategy {
   private static final CMCC instance = new CMCC();
   private CMCCAdapteeService cmccService;
    private CMCC() {
        register();
    }
    public static CMCC getInstance() {
        return instance;
    }
    
     @Override
    public void send(Object... params) {
        CMCCRequest request = new CMCCRequest ();
        // 构建入参
        request.setCMCCReq(params);
        cmccService.specificRequest(request);
    }
}




..........................................

如果使用了Spring框架,还可以利用Spring的Bean机制来代替上述的部分设计,直接使用@Component和@PostConstruct注解即可完成单例的创建和注册,代码会更加简洁。

至此,经过了多次反思和优化,得到了一套低耦合高内聚,同时符合开闭原则的设计。

你可能感兴趣的:(设计模式,策略模式,适配器模式,单例模式,工厂模式)