Skip to content

业务背景

很多场景需要用到条件语句,如果每个条件执行逻辑都比较复杂,或者条件数目较多,直接使用 if-else 会使得代码特别臃肿;

java
/**
 * 根据订单状态获取订单详情页标题
 */
public String getTitle(EOrderStatus status) {
    if (Objects.equals(EOrderStatus.CREATE, status)) {
        return "订单创建成功";
    } else if (Objects.equals(EOrderStatus.CANCEL_BUYER, status) || Objects.equals(EOrderStatus.CANCEL_SELLER, status)) {
        return "订单取消";
    } else if (Objects.equals(EOrderStatus.FINISH, status)) {
        return "订单完成";
    } else {
        // xxx
    }
}

实际情况订单状态达几十种,每个条件下处理逻辑也大不相同,脑补下采用 if-else 画面。

常用做法

策略模式 + 工厂模式

第一步:制定基础抽象策略类;

java
public abstract class AbstractTitleService{

    /**
     * 能处理的订单状态集合
     */
    public abstract EnumSet<EOrderStatus> types();

    public abstract String getTitle();
}

第二步:编写处理工厂类,通过 Spring 扫描 bean 维护到一个 Map 中;

java
@Component
public class TitleServiceFactory implements ApplicationListener<ContextRefreshedEvent> {

    private static final Map<EOrderStatus, AbstractTitleService> serviceMap = new HashMap<>();

    /**
     * 容器初始化完成后,构建处理类 Map
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        ApplicationContext context = contextRefreshedEvent.getApplicationContext();
        Map<String, AbstractTitleService> beans = context.getBeansOfType(AbstractTitleService.class);
        for (AbstractTitleService service : beans.values()) {
            service.types().forEach(type -> serviceMap.put(type, service));
        }
    }

    /**
     * 获取指定状态的处理类
     */
    public static AbstractTitleService getTitleServiceByType(EOrderStatus type) {
        return serviceMap.get(type);
    }
}

第三步:根据不同订单状态划分不同处理类,以订单创建成功为例;

java
@Service
public class CreateTitleService extends AbstractTitleService {

    @Override
    public EnumSet<EOrderStatus> types() {
        return EnumSet.of(EOrderStatus.CREATE);
    }

    @Override
    public String getTitle() {
        return "订单创建成功";
    }
}

如此一来在进行渲染订单详情页标题时,逻辑就会非常简单清晰;

java
public String getTitle(EOrderStatus status) {
    // 获取指定状态的处理类
    AbstractTitleService titleService = TitleServiceFactory.getTitleServiceByType(status);
    return titleService.getTitle();
}

如果需要根据订单状态,渲染详情页操作按钮呢?仍需要按照上面三步依次执行。我们业务只有订单有条件处理逻辑吗?

终版

编写工厂类这一步并不涉及业务流程,可以通过约定简化;

约定所有的基础抽象业务类,需要实现如下接口,并通过泛型限定处理类型枚举;

java
public interface IDispatchService<E extends Enum<E>> {

    /**
     * 支持的处理类型
     */
    EnumSet<E> types();
}

编写全局工厂类,扫描所有 IDispatchService 的处理 bean;

java
@Component
public class DispatchServiceFactory implements ApplicationListener<ContextRefreshedEvent> {

    /**
     * 业务类型(抽象类) -> (条件 -> 处理类)
     * AbstractTitleService -> (CREATE -> CreateTitleService, PAY -> PayTitleService)
     */
    private static final Map<Class<?>, Map<Enum<?>, IDispatchService<?>>> dispatchMap = new HashMap<>();

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        Map<String, IDispatchService> map = context.getBeansOfType(IDispatchService.class);
        for (IDispatchService service : map.values()) {
            Class<?> superclass = service.getClass().getSuperclass();
            if (!dispatchMap.containsKey(superclass)) {
                dispatchMap.put(superclass, new HashMap<>());
            }
            Map<Enum<?>, IDispatchService<?>> serviceMap = dispatchMap.get(superclass);
            EnumSet<?> types = service.types();
            for (Enum<?> type : types) {
                serviceMap.put(type, service);
            }
        }
    }

    public static <T> T getDispatchServiceByType(Class<T> clazz, Enum<?> type) {
        Map<Enum<?>, IDispatchService<?>> serviceMap = dispatchMap.get(clazz);
        return (T) serviceMap.get(type);
    }
}

对于不同的枚举类型,不同的业务类型,全局工厂都能支持,不用再单独编写。

还是以订单详情页标题为例,制定处理基础抽象类;

java
public abstract class AbstractTitleService implements IDispatchService<EOrderStatus> {

    public abstract String getTitle();
}

根据不同订单状态划分不同处理类,以订单创建成功为例;

java
@Service
public class CreateTitleService extends AbstractTitleService {
    @Override
    public EnumSet<EOrderStatus> types() {
        return EnumSet.of(EOrderStatus.CREATE);
    }

    @Override
    public String getTitle() {
        return "订单创建成功";
    }
}

使用时除了枚举类型,还需要指定处理超类;

java
public String getTitle(EOrderStatus status) {
    AbstractTitleService titleService = DispatchServiceFactory.getDispatchServiceByType(AbstractTitleService.class, status);
    return titleService.getTitle();
}

总结

if-else 在条件数目较多处理逻辑复杂时可读性较差,可以用“策略模式”+“工厂模式”消除 if-else。由于工厂逻辑的通用性,根据约定好的处理接口制定全局工厂类,最大程度减少编码量。

基于 MIT 许可发布