需要关于重构大型 java switch-case 的建议

need advice on refactoring a large java switch-case

我有一个我真的不喜欢的大开关盒,但我似乎没有找到一个优雅的替代解决方案。 我们正在构建一个 JavaEE 平台,用户可以在其中创建项目。列出的方法用于确定向我们的用户显示的状态消息,这取决于许多因素,例如用户类型(我们有 2 个)、项目状态、项目的付款状态等等。这些因素中的大部分都必须以编程方式确定,最终导致了这个大的 switch-case:

public List<String> getToDoMessages(Project project) {
    UserAccount user = securitySession.getLoggedInAccount();
    switch (user.getAccountType()) {
        case EXPERT:
            return getExpertToDoMessages(project, user);
        case COMPANY:
            return getCompanyToDoMessages(project, user);
        default:
            return new ArrayList<>();
    }
}

private List<String> getCompanyToDoMessages(Project project, UserAccount user) {
    List<String> ret = new ArrayList<>();
    switch (project.getProjectState()) {
        case OPEN_FOR_APPLICATION:
            ret.add("projectToDo_company_applicationDeadlineNotPassed");
            break;
        case SELECT_APPLICATION:
            ret.add("projectToDo_company_selectApplicant");
            break;
        case IN_PROGRESS:
            ret.add("projectToDo_company_inProgress");
            break;
        case TEAM_PAYMENT_DISTRIBUTION:
            ret.add("projectToDo_company_teamPaymentDistribution");
            break;
        case CONFIRM_INVOICES:
            if (projectAssessmentService.hasAssessed(user, project)) {
                ret.add("projectToDo_company_confirmInvoices");
            } else {
                ret.add("projectToDo_company_assessProject");
            }
            break;
        default:
            break;
    }
    return ret;
}

private List<String> getExpertToDoMessages(Project project, UserAccount user) {
    ExpertPerson expert = securitySession.getExpert();
    List<String> ret = new ArrayList<>();
    switch (project.getProjectState()) {
        case OPEN_FOR_APPLICATION:
            if (projectService.hasAlreadyApplied(expert, project)) {
                if (projectService.hasAlreadyAppliedAsPerson(expert, project)) {
                    ret.add("projectToDo_expert_appliedAsSinglePerson");
                }
                if (projectService.hasAlreadyAppliedAsTeam(expert, project)) {
                    ret.add("projectToDo_expert_appliedAsTeam");
                }

            } else {
                if (projectService.canApply(expert, project)) {
                    ret.add("projectToDo_expert_openForApplication");
                }
            }
            break;
        case SELECT_APPLICATION:
            ret.add("projectToDo_expert_selectApplicant");
            break;
        case IN_PROGRESS:
            ret.add("projectToDo_expert_inProgress");
            break;
        case TEAM_PAYMENT_DISTRIBUTION:
            Application application = project.getSelectedApplication();
            if (application.isSingleApplication()) {
                throw new IllegalStateException("Illegal state TEAM_PAYMENT_DISTRIBUTION for project that has selected a single application");
            }
            ExpertTeam team = application.getExpertTeam();
            if (team.getLeader().equals(expert)) {
                ret.add("projectToDo_expert_teamLeaderPaymentDistribution");
            } else {
                ret.add("projectToDo_expert_teamMemberPaymentDistribution");
            }
            break;
        case CONFIRM_INVOICES:
            if (projectAssessmentService.hasAssessed(user, project)) {
                ret.add("projectToDo_expert_confirmInvoices");
            } else {
                ret.add("projectToDo_expert_assessProject");
            }
            break;
        default:
            break;
    }
    return ret;
}

这个版本没有列出所有的可能性,例如项目类型的区别仍然缺失。当然,我至少可以将语句中的代码移动到单独的方法中,但我确信必须有更优雅的解决方案。有谁知道可能适用于此的好模式?

提前致谢!

重构 switch 语句可以采用三种好的方法。

  1. 使用 Map。这允许您预先构建工具,通常甚至允许您从外部配置文件配置工具。这里的缺点是您必须跳过一些障碍才能添加逻辑。
  2. 使用 enum。这比 Map 更灵活,因为您可以在每个 enum 中编写逻辑代码,但也有一些缺点。有些人认为 enum 中的编码逻辑是一件坏事(我不这么认为)。另外,enums 只能是 static 的事实会让你的工作变得不那么容易。
  3. 使用多态性。让你的 Project 对象有一个 getToDoMessages 方法等。这可能会导致一些相当复杂的管理问题,因为所有 getToDoMessages 方法都分布在你的代码中,而不是像你那样分布在一个模块中现在 - 但请将此选项视为一个很好的选项,因为它通常是最灵活的选项。

Map 路线示例:

Map<Integer,String> companyToDos = new HashMap<>();
static {
    companyToDos.put(OPEN_FOR_APPLICATION, "projectToDo_company_applicationDeadlineNotPassed");
    companyToDos.put(SELECT_APPLICATION, "projectToDo_company_selectApplicant");
    companyToDos.put(IN_PROGRESS, "projectToDo_company_inProgress");
    companyToDos.put(TEAM_PAYMENT_DISTRIBUTION, "projectToDo_company_teamPaymentDistribution");
    companyToDos.put(CONFIRM_INVOICES_ASSESSED, "projectToDo_company_confirmInvoices");
    companyToDos.put(CONFIRM_INVOICES_UNASSESSED, "projectToDo_company_assessProject");
}

enum路线的例子:

enum AccountType {
    EXPERT{

        @Override
        List<String> getToDoMessages(Project project) {
            return project.getState().getExpertToDoMessages();
        }

    },
    COMPANY{

        @Override
        List<String> getToDoMessages(Project project) {
            return project.getState().getCompanyToDoMessages();
        }

    };

    abstract List<String> getToDoMessages(Project project);

}

enum ProjectState {
    OPEN_FOR_APPLICATION{

        @Override
        List<String> getExpertToDoMessages(Project project) {
            return ...
        }

        @Override
        List<String> getCompanyToDoMessages(Project project) {
            return ...
        }

    },
    SELECT_APPLICATION{

        @Override
        List<String> getExpertToDoMessages(Project project) {
            return ...
        }

        @Override
        List<String> getCompanyToDoMessages(Project project) {
            return ...
        }

    };

    abstract List<String> getExpertToDoMessages(Project project);
    abstract List<String> getCompanyToDoMessages(Project project);

}