需要关于重构大型java开关案例的建议

时间:2015-02-24 09:57:56

标签: java switch-statement refactoring

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

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;
}

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

提前致谢!

1 个答案:

答案 0 :(得分:4)

重构switch语句有三条好路径。

  1. 使用Map。这允许您预先构建工具,甚至可以允许您从外部配置文件配置工具。这里的缺点是你必须跳过一些箍来增加逻辑。
  2. 使用enum。这可能比Map更灵活,因为您可以在每个enum内编码逻辑,但有一些缺点。有些人认为enum中的编码逻辑是一件坏事(我没有)。此外,enum s只能static这一事实可能会让您的工作变得不那么容易。
  3. 使用多态。使您的Project对象具有getToDoMessages方法等。这可能会导致一些非常复杂的管理问题,因为所有getToDoMessages方法都分布在整个代码中,而不是分布在一个模块中他们现在 - 但请将此选项视为一个好选项,因为它通常是最灵活的选项。
  4. 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);
    
    }