Java 处理复杂条件求值的设计模式

Java 处理复杂条件求值的设计模式,java,design-patterns,refactoring,conditional,Java,Design Patterns,Refactoring,Conditional,我的设计目的是维护一个系统,该系统考虑三个变量的值,以确定将采取的行动 我想重构它以使用设计模式,但找不到适合它需要的模式 为了解释这种情况,我将以健身房系统为例 每个健身房用户都有一个类型的合同,可以是: 白金会员 黄金会员 银联会员 健身房有一些健身班: 举重 身体平衡 台阶 旋转 尊巴 个人培训 每个健身房使用者都有身体状况 无限制 65岁以上 流动性有限 医疗条件 18岁以下 对于这三个特征的每一个组合,都应该执行一组任意的操作。例如: 如果白金会员+个人培训+65岁以上:

我的设计目的是维护一个系统,该系统考虑三个变量的值,以确定将采取的行动

我想重构它以使用设计模式,但找不到适合它需要的模式

为了解释这种情况,我将以健身房系统为例

每个健身房用户都有一个类型的合同,可以是:

  • 白金会员
  • 黄金会员
  • 银联会员
健身房有一些健身班:

  • 举重
  • 身体平衡
  • 台阶
  • 旋转
  • 尊巴
  • 个人培训
每个健身房使用者都有身体状况

  • 无限制
  • 65岁以上
  • 流动性有限
  • 医疗条件
  • 18岁以下
对于这三个特征的每一个组合,都应该执行一组任意的操作。例如:

如果白金会员+个人培训+65岁以上:

  • 需要医疗批准
  • 签名表格
  • 如果黄金会员+个人培训+65岁以上:

  • 需要医疗批准
  • 签名表格
  • 额外月费
  • 如果银牌会员+个人培训+65岁以上:

  • 拒绝订阅
  • 如果(任何会员)+步骤+医疗条件:

  • 需要医疗批准
  • 签名表格
  • 如果白金会员资格+举重+有限的活动性:

  • 需要医疗批准
  • 签名表格
  • 专职工作人员协助
  • 等等

    特征的组合可以有一组动作,这些动作不是排他性的,并且并非所有的组合都能得到保证

    遗留代码使用嵌套开关作为实现。例如:

    switch (contractType):
    
        case PLATINUM_MEMBERSHIP:
    
            switch (gymClass):            
    
                case (PERSONAL_TRAINING):
    
                    switch (physicalCondition):            
    
                        case (OVER_65):
    
                            requiresMedicalApproval();
                            requiresSignedForm();
    
    ...
    
    我的问题是:

    • 有3个条件组合起来定义一组规则
    • 这些规则不一定是唯一的
    • 不是每个组合都定义了一个集合
    我使用提取方法技术进行了一点重构,并对代码进行了一点清理,但无法摆脱3个开关

    我希望使用设计模式来改进设计,但到目前为止我没有成功

    我考虑过多态性和策略,但想不出使用它们的方法

    我也在谷歌上做过研究,但还没有找到任何有用的东西

    你有什么建议

    多谢各位


    编辑:

    在研究@Paul的决策树方法时,我找到了一个解决方案。在使用决策树进行测试之后,我尝试使用三维数组来定义规则的条件。我还使用命令模式定义了规则激活时需要执行的操作

    简言之:

    1) 枚举以定义变量:

    public enum TypeOfContract { ... }
    public enum GymClasses { ... }
    public enum PhysicalCondition { ... }
    
    所有可能的条件都将放在枚举中

    2) 用于定义操作的命令界面

    public interface Command {
        public void execute(Map<String, Object> parametersMap);
    }
    
    (为了在该站点中可视化而进行的异常代码格式化)

    在这里,变量的有意义关联在规则三维数组中定义

    这些规则是通过使用相应的枚举来定义的

    对于我给出的第一个例子,白金会员+个人培训+65岁以上,以下内容适用:

    RULES
        [TypeOfContract.PLATINUM_MEMBERSHIP.ordinal()]
        [GymClasses.PERSONAL_TRAINING.ordinal()]
        [PhysicalCondition.OVER_65.ordinal()]
    
    (需要使用ordinal()返回对应于枚举位置的int)

    为了表示需要执行的操作,关联了一个Procedures类,它包装了要执行的操作:

    new Procedures(new RequireMedicalApproval(), new RequireSignedForm() );
    
    RequireMedicalApproval和RequiremedignedForm都实现了命令接口

    定义这种变量组合的整个过程是:

    RULES
            [TypeOfContract.PLATINUM_MEMBERSHIP.ordinal()]
            [GymClasses.PERSONAL_TRAINING.ordinal()]
            [PhysicalCondition.OVER_65.ordinal()] =
                new Procedures(new RequireMedicalApproval(), 
                               new RequireSignedForm() );
    
    要检查特定组合是否具有关联的操作,将调用
    loadProcedures
    ,传递表示该特定组合的枚举

    5) 用法

    Map context=newhashmap();
    context.put(“userId”,123);
    上下文。put(“缩写”、“C45354”);
    context.put(“userDetails”,userDetails);
    上下文。put(“合同类型”,合同类型。白金联盟成员资格);
    上下文。put(“体操课”,体操课。个人训练);
    上下文.put(“PhysicalCondition”,PhysicalCondition.OVER_65);
    ...
    程序loadedProcedures=RulesEngine.loadProcedures(
    合同类型。白金联盟成员资格,
    体操课,个人训练,
    身体状况(65岁以上);
    for(命令操作:loadedProcedures.getActionsToExecute()){
    行动。平等(上下文);
    }
    
    操作需要执行的所有信息现在都在映射中

    由三个枚举表示的条件将传递给RulesEngine

    RulesEngine将评估组合是否具有关联的操作,并返回一个过程对象,其中包含需要执行的这些操作的列表

    如果没有(组合没有关联的操作),RulesEngine将返回一个带有空列表的有效过程对象

    6) 专业人士

    • 使用代码要干净得多
    • 遗留代码的交换机中的代码复制现在消失了
    • 这些动作现在已经标准化并得到了很好的定义(每个动作都在自己的类中)
    • 现在使用的规则更容易识别(开发人员只需查看规则数组,就可以知道设置了哪些规则以及每个规则中会发生什么)
    • 可以轻松添加新规则和操作
    7) 缺点

    • 规则的定义很容易出错,因为规则的声明很冗长,没有进行语义分析——例如,可以接受重复,可能会覆盖以前的定义
    • 现在我有了几个类,而不是嵌套在彼此内部的3个开关。维修
      new Procedures(new RequireMedicalApproval(), new RequireSignedForm() );
      
      RULES
              [TypeOfContract.PLATINUM_MEMBERSHIP.ordinal()]
              [GymClasses.PERSONAL_TRAINING.ordinal()]
              [PhysicalCondition.OVER_65.ordinal()] =
                  new Procedures(new RequireMedicalApproval(), 
                                 new RequireSignedForm() );
      
          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);
          }