一、概述
我们工作的业务代码从注释、命名、方法和异常等多方面实现整洁代码,但是感觉不够简洁,不够优雅,因为这是设计上的不足,总结就是抽象不够、可读性低、不够健壮。那这些就用到了设计模式。
java本身没有设计模式,但是随着时代的发展,写代码是人多了,他们便总结出了一套能提高开发和维护效率的套路,这就是设计模式。设计模式不是什么教条或者范式,它可以说是一种在特定场景下普适且可复用的解决方案,是一种可以用于提高代码可读性、可扩展性、可维护性的最佳实践。
二、短信发送策略
我目前在职的公司主营业务是智能语音外呼,主要是用机器人语音外呼替代人工,为了提升外呼之后的转换,通常会给客户发送一条挂机短信。但是因为通信通道的不稳定性以及业务复杂性(每个通道能发送的短信模板都是不一样的,比如营销类短信,游戏类短信),导致我们经常要对接各种短信通道。多数的小伙伴就会写出以下的代码:
if(type=="A通道"){
//按照A通道的对接文档进行对接
}else if(type=="B通道"){
//按照B通道的对接文档进行对接
}else{
//按照默认格式解析
}
这个代码可能会存在哪些问题呢?
说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则、单一原则以及迪米特法则。
如果你的代码就是:有多个if…else等条件分支,并且每个条件分支,可以封装起来替换的,这个案例就可以使用策略模式和适配器模式来优化。
首先是策略模式:策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。
策略模式通常包含以下角色:
然后是适配器模式:适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式包含以下主要角色:
// 策略接口
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注解即可完成单例的创建和注册,代码会更加简洁。
至此,经过了多次反思和优化,得到了一套低耦合高内聚,同时符合开闭原则的设计。