【问题标题】:need advice on refactoring a large java switch-case需要有关重构大型 java switch-case 的建议
【发布时间】:2015-02-24 09:57:55
【问题描述】:

我有一个我真的不喜欢的大开关盒,但我似乎没有找到一个优雅的替代解决方案。 我们正在构建一个 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;
}

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

提前致谢!

【问题讨论】:

  • 使用多态性代替开关
  • 这在我们的情况下相当困难,我们不能将此方法移动到任何实体类中,因为我们需要依赖注入来实现某些区别。此外,这可能只会解决一个级别的 switch-case(当然总比没有好)
  • 你可以编辑枚举类吗?

标签: java switch-statement refactoring


【解决方案1】:

重构 switch 语句有 3 种好方法。

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

}

【讨论】:

  • 感谢您提供的精彩示例!我查看了所有案例,对于其中的 95%,枚举路由将是一个替代方案。但是,我们仍然有一些逻辑不能放在枚举或实体类中,需要注入。因此,我将暂时离开当前状态,因为这也可以防止枚举混乱。这样一来,我们仍然一团糟,但它仅限于一个模型,从我的角度来看这是可以接受的,因为枚举已经非常膨胀(本示例中并未列出所有状态)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
相关资源
最近更新 更多