Qua*_*cem 5 java refactoring design-patterns conditional-statements
我的目的是维护一个系统,该系统考虑三个变量的值来确定将采取哪个操作。
我想重构它以使用设计模式,但找不到适合它需要的设计模式。
为了解释这种情况,我将以健身房系统为例。
每个健身房用户都有一个TYPE_OF_CONTRACT,可能是:
健身房有一些GYM_CLASSES:
每个健身房用户都有一个PHYSICAL_CONDITION
对于这三个特征的每种组合,都应该执行一组任意操作。例如:
如果是 PLATINUM_MEMBERSHIP + PERSONAL_TRAINING + OVER_65:
如果是 GOLD_MEMBERSHIP + 个人培训 + OVER_65:
如果是 SILVER_MEMBERSHIP + PERSONAL_TRAINING + OVER_65:
如果(任何会员资格)+ 步骤 + 医疗条件:
如果 PLATINUM_MEMBERSHIP + WEIGHT_LIFTING + LIMITED_MOBILITY:
等等。
特征的组合可以具有一组动作,这些动作不是排他性的并且并非所有组合都得到保证。
遗留代码使用嵌套开关作为实现。例子:
switch (contractType):
case PLATINUM_MEMBERSHIP:
switch (gymClass):
case (PERSONAL_TRAINING):
switch (physicalCondition):
case (OVER_65):
requiresMedicalApproval();
requiresSignedForm();
...
Run Code Online (Sandbox Code Playgroud)
我的问题是:
我使用提取方法技术进行了一些重构并稍微清理了代码,但无法摆脱这 3 个开关。
我希望使用设计模式来改进设计,但到目前为止我还没有成功。
我考虑过多态性和策略,但无法找到使用它们的方法。
我也在谷歌上进行了研究,但没有找到任何我可以使用的东西。
你有什么建议?
谢谢。
编辑:
我在研究 @Paul 的决策树方法时达成的解决方案。在使用决策树进行测试后,我尝试了一个三维数组来定义规则的条件。我还使用命令模式来定义激活规则时需要执行的操作。
简单来说:
1) 枚举来定义变量:
public enum TypeOfContract { ... }
public enum GymClasses { ... }
public enum PhysicalCondition { ... }
Run Code Online (Sandbox Code Playgroud)
所有可能的条件都会被放入枚举中。
2)定义操作的命令接口
public interface Command {
public void execute(Map<String, Object> parametersMap);
}
Run Code Online (Sandbox Code Playgroud)
每一个行动都是命令的执行。Map 参数将用于将运行时上下文传递给方法。
3) 一个程序类,用于保存每个条件所需的操作。
public class Procedures {
private List<Command> actionsToExecute = new LinkedList<Command>();
public static final Procedures NO_ACTIONS_TO_EXECUTE = new Procedures();
private Procedures() {}
public Procedures(Command... commandsToExecute) {
if (commandsToExecute == null || commandsToExecute.length == 0) {
throw new IllegalArgumentException("Procedures must have at least a command for execution.");
}
for (Command command : commandsToExecute) {
actionsToExecute.add(command);
}
}
public List<Command> getActionsToExecute() {
return Collections.unmodifiableList(this.actionsToExecute);
}
}
Run Code Online (Sandbox Code Playgroud)
procedures 类代表需要执行的命令。它有一个命令的链表,以确保命令按所需的顺序执行。
如果三个变量的组合不存在,它会发送 NO_ACTIONS_TO_EXECUTE 而不是 null。
4)RulesEngine类,用于注册规则及其命令
public class RulesEngine {
private static final int NUMBER_OF_FIRST_LEVEL_RULES = TypeOfContract.values().length;
private static final int NUMBER_OF_SECOND_LEVEL_RULES = GymClasses.values().length;
private static final int NUMBER_OF_THIRD_LEVEL_RULES = PhysicalCondition.values().length;
private static final Procedures[][][] RULES =
new Procedures[NUMBER_OF_FIRST_LEVEL_RULES]
[NUMBER_OF_SECOND_LEVEL_RULES]
[NUMBER_OF_THIRD_LEVEL_RULES];
{ //static block
RULES
[TypeOfContract.PLATINUM_MEMBERSHIP.ordinal()]
[GymClasses.PERSONAL_TRAINING.ordinal()]
[PhysicalCondition.OVER_65.ordinal()] =
new Procedures(new RequireMedicalApproval(),
new RequireSignedForm() );
RULES
[TypeOfContract.GOLD_MEMBERSHIP.ordinal()]
[GymClasses.PERSONAL_TRAINING.ordinal()]
[PhysicalCondition.OVER_65.ordinal()] =
new Procedures(new RequireMedicalApproval(),
new RequireSignedForm(),
new AddExtraMonthlyFee() );
...
}
private RulesEngine() {}
public static Procedures loadProcedures(TypeOfContract TypeOfContract,
GymClasses GymClasses, PhysicalCondition PhysicalCondition) {
Procedures procedures = RULES
[TypeOfContract.ordinal()]
[GymClasses.ordinal()]
[PhysicalCondition.ordinal()];
if (procedures == null) {
return Procedures.NO_ACTIONS_TO_EXECUTE;
}
return procedures;
}
}
Run Code Online (Sandbox Code Playgroud)
(为了本网站的可视化目的,进行了不寻常的代码格式化)
这里变量的有意义的关联是在 RULES 三维数组中定义的。
这些规则是通过使用相应的枚举来定义的。
对于我给出的第一个示例,PLATINUM_MEMBERSHIP + PERSONAL_TRAINING + OVER_65,适用以下内容:
RULES
[TypeOfContract.PLATINUM_MEMBERSHIP.ordinal()]
[GymClasses.PERSONAL_TRAINING.ordinal()]
[PhysicalCondition.OVER_65.ordinal()]
Run Code Online (Sandbox Code Playgroud)
(需要ordinal()返回与枚举位置对应的int)
为了表示需要执行的操作,关联了一个过程类,包装了要执行的操作:
new Procedures(new RequireMedicalApproval(), new RequireSignedForm() );
Run Code Online (Sandbox Code Playgroud)
RequireMedicalApproval 和 RequireSignedForm 都实现 Command 接口。
定义这个变量组合的整行代码是:
RULES
[TypeOfContract.PLATINUM_MEMBERSHIP.ordinal()]
[GymClasses.PERSONAL_TRAINING.ordinal()]
[PhysicalCondition.OVER_65.ordinal()] =
new Procedures(new RequireMedicalApproval(),
new RequireSignedForm() );
Run Code Online (Sandbox Code Playgroud)
要检查特定组合是否具有与其关联的操作,请loadProcedures调用 ,并传递表示该特定组合的枚举。
5) 使用方法
Map<String, Object> context = new HashMap<String, Object>();
context.put("userId", 123);
context.put("contractId", "C45354");
context.put("userDetails", userDetails);
context.put("typeOfContract", TypeOfContract.PLATINUM_MEMBERSHIP);
context.put("GymClasses", GymClasses.PERSONAL_TRAINING);
context.put("PhysicalCondition", PhysicalCondition.OVER_65);
...
Procedures loadedProcedures = RulesEngine.loadProcedures(
TypeOfContract.PLATINUM_MEMBERSHIP,
GymClasses.PERSONAL_TRAINING,
PhysicalCondition.OVER_65);
for (Command action : loadedProcedures.getActionsToExecute()) {
action.equals(context);
}
Run Code Online (Sandbox Code Playgroud)
操作需要执行的所有信息现在都位于地图内。
由三个枚举表示的条件被传递到规则引擎。
RulesEngine 将评估该组合是否具有关联的操作,并将返回一个 procedures 对象,其中包含需要执行的这些操作的列表。
如果不是(该组合没有与之关联的操作),RulesEngine 将返回一个带有空列表的有效过程对象。
6) 优点
7) 缺点
| 归档时间: |
|
| 查看次数: |
3091 次 |
| 最近记录: |