几种消除if/else的方式

本文探讨了在项目复杂度增加时,如何重构充斥大量if/else和switch/case的代码。通过枚举、工厂模式和策略模式举例,展示了如何优化逻辑判断,减少代码维护成本。同时,提到了使用Optional类消除空指针判断以及结合Spring使用策略模式的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

随着项目的越来越复杂,条件分支越来越多,代码充斥着大量的if/else和switch/case判断,甚至是多层嵌套的if/else,我们需要重新重构或者组织逻辑代码。

先看随手写的一个根据渠道类型推送消息例子。

public boolean pushByType(String type, String msg) {
    if (type == null) return false;
    if ("sms".equals(type)) return smsPushService.push(msg);
    else if ("email".equals(type)) return emailPushService.push(msg);
    else return wechatPushService.push(msg);
}

从这段可以看到,函数需要对传入进行校验,这里是一个分支,根据类型选择推送类型,这里有三个分支,如果之后有新的渠道,那我们就又需要修改这里的代码,增加判断分支,导致函数越来越复杂…

所以随着项目越来越复杂,后续有重构的时候要多考虑如何消除这里判断分支,当然,不是所有的if/else都要消除,比如上面的null值判断。总结:

  • 对于null或boolean判断,大多时候不需要管
  • 对于变量的状态有多种,并且将来还会增加的情况,就需要做优化
  • 对于对于if/else中再嵌套if/else的情况,可以逆向逻辑判断,将内部的逻辑提取到同一级后方
  • 使用switch/case一般都意味着多种状态,不然你也用不着是吧

消除if/else有多种方法,设计模式中的工厂模式和策略模式就可以用来处理这个问题,此外利用枚举类的特性也可以,下面具体看看这几种方法。

枚举

在枚举类的基础上扩展,将业务实现类通过枚举类构造函数传进来,这样就把类型和对应的处理方法关联起来。坏处是枚举类变得臃肿起来,通常我们只是用枚举类来定义一组常量。

public enum PushChannel {
    sms(PushService.smsPush()),
    email(PushService.emailPush()),
    we_chat(PushService.wechatPush());

    PushService pushService;

    private PushChannel(PushService pushService) {
        this.pushService = pushService;
    }

    public PushService getPushService() {
        return pushService;
    }
}

public interface PushService {
    public boolean push(String msg);

    public static PushService smsPush() {
        return new PushService() {
            public boolean push(String msg) {
                System.out.println("sms:" + msg);
                return true;
            }
        };
    }
    // public static PushService emailPush() ...
    // public static PushService wechatPush() ...
}

调用测试:

@Test
public void test_enum() {
    boolean push = PushChannel.valueOf("sms").getPushService().push("Hello.");
    Assert.assertEquals(true, push);
}

工厂模式

接口。

public interface PushService {
    boolean push(String msg);
}

这里的枚举类就不干那么多活了。

enum PushChannel {
    sms("sms"),
    email("email"),
    wechat("wechat");

    String channel;
    PushChannel(String channel) {
        this.channel = channel;
    }

    String getVal() {
        return channel;
    }
}

/**
 * 推送渠道有短信、邮件、微信等等......
 * 懒得新建类文件所以直接写在这里一起
 */
class SmsPushService implements PushService {
    @Override
    public boolean push(String msg) {
        System.out.println("sms:" + msg);
        return true;
    }
}
// EmailPushService...
// WeChatPushService...

工厂类,对于一些空指针判断还可以借助Optional类来消除。

public class PushFactory {
    private static Map<String, PushService> pushMap = new HashMap<>();

    static {
        pushMap.put(PushChannel.sms.getVal(), new SmsPushService());
        pushMap.put(PushChannel.email.getVal(), new EmailPushService());
        pushMap.put(PushChannel.wechat.getVal(), new WeChatPushService());
    }

    // 常规方法
    public static PushService getPushService(String type) {
        PushService pushService = pushMap.get(type);
        if (pushService == null) throw new NoSuchElementException();
        return pushService;
    }
    // 内部有空指针判断可借助Optional类
    public static PushService getPushServiceOptional(String type) {
        
        return Optional.ofNullable(pushMap.get(type))
                .orElseThrow(() -> new NoSuchElementException());
    }
}

单元测试跑看看。

@Test
public void test() {
    boolean sms = PushFactory.getPushService("sms").push("sms msg.");
    Assert.assertEquals(true, sms);

    boolean email = PushFactory.getPushService("email").push("email msg.");
    Assert.assertEquals(true, email);

    Assert.assertThrows(NoSuchElementException.class, () -> PushFactory.getPushService("gms"));
}

@Test
public void test_optional() {
    boolean email = PushFactory.getPushServiceOptional("email").push("email msg.");
    Assert.assertEquals(true, email);

    Assert.assertThrows(NoSuchElementException.class, () -> PushFactory.getPushServiceOptional("gms"));
}

策略模式

策略的实现跟工厂很像,只不过工厂是生产对象再自己执行函数,策略则是行为型模式,直接执行行为。

这里顺便结合下Spring看看是如何使用的。

Strategy接口和枚举类型。

public interface PushService {
    PushChannel type();
    boolean push(String msg);
}
public enum PushChannel {
    sms,
    email,
    wechat;
}

各实现类。

@Service
public class SmsPushService implements PushService {
    @Override
    public boolean push(String msg) {
        System.out.println("sms:" + msg);
        return true;
    }

    @Override
    public PushChannel type() {
        return PushChannel.sms;
    }
}
// EmailPushService...
// WeChatPushService...

可以用列表或者哈希表的方式注入,两种选一种就行了

@Component
public class PushServiceInterfaceContext {

    private final List<PushService> serviceList;

    // 提示 field injection is not recommended,所以用构造注入
    public PushServiceInterfaceContext(List<PushService> serviceList) {
        this.serviceList = serviceList;
    }

    public boolean apply(String type, String msg) {
        Optional<PushService> pushService = serviceList.stream()
                .filter(service -> service.type() == PushChannel.valueOf(type))
                .findAny();
        PushService service = pushService.orElseThrow(() -> new NoSuchElementException());
        return service.push(msg);
    }
}


@Component
public class PushServiceMapContext {
    private final Map<String, PushService> serviceMap = new ConcurrentHashMap<>();

    public PushServiceMapContext(Map<String, PushService> serviceMap) {
        this.serviceMap.clear();
        this.serviceMap.putAll(serviceMap);
    }

    public boolean apply(String type, String msg) {
        PushService service = serviceMap.get(type + "PushService");
        return service.push(msg);
    }
}

使用方式。

PushServiceInterfaceContext pushServiceInterfaceContext;
@Autowired
BizController(PushServiceInterfaceContext pushServiceInterfaceContext) {
    this.pushServiceInterfaceContext = pushServiceInterfaceContext;
}
//...
//调用
pushServiceInterfaceContext.apply(type, msg);

源码地址。

写完了代码还是稍微有点多,在两三个分支的情况下其实用不上这里模式,搞得更复杂了。简单的东西就不用设计过度,过早优化,复杂的业务也要权衡一下,如何是经常变动修改,持续维护的情况那就值了。


原文链接。访问我的个人网站查看更多文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值