需要关于重构大型 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
语句可以采用三种好的方法。
- 使用
Map
。这允许您预先构建工具,通常甚至允许您从外部配置文件配置工具。这里的缺点是您必须跳过一些障碍才能添加逻辑。
- 使用
enum
。这比 Map
更灵活,因为您可以在每个 enum
中编写逻辑代码,但也有一些缺点。有些人认为 enum
中的编码逻辑是一件坏事(我不这么认为)。另外,enum
s 只能是 static
的事实会让你的工作变得不那么容易。
- 使用多态性。让你的
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);
}
我有一个我真的不喜欢的大开关盒,但我似乎没有找到一个优雅的替代解决方案。 我们正在构建一个 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
语句可以采用三种好的方法。
- 使用
Map
。这允许您预先构建工具,通常甚至允许您从外部配置文件配置工具。这里的缺点是您必须跳过一些障碍才能添加逻辑。 - 使用
enum
。这比Map
更灵活,因为您可以在每个enum
中编写逻辑代码,但也有一些缺点。有些人认为enum
中的编码逻辑是一件坏事(我不这么认为)。另外,enum
s 只能是static
的事实会让你的工作变得不那么容易。 - 使用多态性。让你的
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);
}