【问题标题】:Are there any techniques to split a method with a flag argument?是否有任何技术可以拆分带有标志参数的方法?
【发布时间】:2010-11-24 11:41:21
【问题描述】:

我有一个带有标志参数的方法。我认为将布尔值传递给方法是一种不好的做法(使签名复杂化,违反了“每个方法做一件事”的原则)。我认为将方法分成两种不同的方法更好。但是如果我这样做,这两种方法会非常相似(代码重复)。

我想知道是否有一些通用技术可以将带有标志参数的方法拆分为两个单独的方法。

这是我的方法(Java)的代码:

int calculateNumOfLiveOrDeadNeighbors(Cell c, int gen, boolean countLiveOnes) {
   int x = c.getX();
   int y = c.getY();
   CellState state;
   int aliveCounter = 0;
   int deadCounter = 0;
   for (int i = x - 1; i <= x + 1; i++) {
      for (int j = y - 1; j <= y + 1; j++) {
         if (i == x && j == y)
            continue;
         state = getCell(i, j).getCellState(gen);
         if (state == CellState.LIVE || state == CellState.SICK){
            aliveCounter++;
         }
         if(state == CellState.DEAD || state == CellState.DEAD4GOOD){
            deadCounter++;
         }
      }
   }
   if(countLiveOnes){
      return aliveCounter;
   }
   return deadCounter;
}

【问题讨论】:

    标签: java refactoring flags coding-style


    【解决方案1】:

    如果您不喜欢签名上的布尔值,您可以添加两种不带它的不同方法,重构为 private 主要方法:

    int calculateNumOfLiveNeighbors(Cell c, int gen) {
      return calculateNumOfLiveOrDeadNeighbors(c, gen, true);
    }
    int calculateNumOfDeadNeighbors(Cell c, int gen) {
      return calculateNumOfLiveOrDeadNeighbors(c, gen, false);
    }
    

    您可以编写一个 Result Classint 数组 作为输出参数来存储这两个结果;这将使您摆脱烦人的布尔参数。

    【讨论】:

    • 我更喜欢带有公共委托方法的私有方法,而不是其他任何建议。
    【解决方案2】:

    我想这取决于每个案例。

    在我看来,在这个例子中你有两个选择。

    说你想拆分通话calculateNumOfLiveOrDeadNeighbors()

    在两个:

    calculateNumOfLiveNeighbors() 
    

    calculateNumOfDeadNeighbors()
    

    您可以使用Template Method 将循环移动到另一种方法。 您可以使用它来计算两种方法中的死/活细胞。

    private int countCells(Cell c, int gen, Filter filter)
    {
        int x = c.getX();
        int y = c.getY();
        CellState state;
        int counter = 0;
        for (int i = x - 1; i <= x + 1; i++) 
        {
            for (int j = y - 1; j <= y + 1; j++) 
            {
                if (i == x && j == y)
                    continue;
                state = getCell(i, j).getCellState(gen);
                if (filter.countMeIn(state))
                {
                    counter++;
                }
            }
        }
        return counter;
     }
    
     private interface Filter
     {
          boolean countMeIn(State state);
     }
    
     public int calculateNumOfDeadNeighbors(Cell c, int gen)
     {
         return countCells(c, gen, new Filter()
                           { 
                               public boolean countMeIn(CellState  state)
                               {
                                  return (state == CellState.DEAD || state == CellState.DEAD4GOOD);
                               }
                            });
      }
    
     public int calculateNumOfLiveNeighbors(Cell c, int gen)
     {
         return countCells(c, gen, new Filter()
                           { 
                               public boolean countMeIn(CellState  state)
                               {
                                  return (state == CellState.LIVE || state == CellState.SICK);
                               }
                            });
      }
    

    这很麻烦,甚至不值得痛苦。或者,您可以使用monad 来存储统计计算的结果,然后在monad 上使用getDeadCounter()getLiveCounter(),正如许多人已经建议的那样。

    【讨论】:

    • 好主意,但是使用静态内部类来实现过滤器。
    【解决方案3】:
    • 您可以尝试在单一方法中提取常用功能并仅使用特定功能
    • 您可以使用该标志创建一个私有方法,并从两个公共方法调用它。因此,您的公共 API 不会有“复杂”的方法签名,也不会有重复的代码
    • 创建一个返回两个值的方法,并在每个调用者中选择一个(公共方法)。

    在上面的例子中,我认为第二个和第三个选项更适用。

    【讨论】:

      【解决方案4】:

      似乎最语义干净的方法是返回一个包含两个值的结果对象,并让调用代码从结果对象中提取它关心的内容。

      【讨论】:

      • +1 用于优化。否则,该方法应该被调用两次以获得死的和活着的邻居。
      【解决方案5】:

      就像博卓说的:但是,但是将第 2 点和第 3 点以相反的方式结合起来:

      创建一个(可能的私有方法),它同时返回(活的和死的)和(仅当您在大多数情况下需要单独的死或活时)然后添加两个从结果中选择死或两者的方法:

      DeadLiveCounter calcLiveAndDead(..) {}
      int calcLive(..) { return calcLiveAndDead(..).getLive; }
      int calcDead(..) { return calcLiveAndDead(..).getDead; }
      

      【讨论】:

        【解决方案6】:

        IMO,这个所谓的“每个方法做一件事”的原则需要有选择地应用。您的示例是其中一个,最好不要应用它。相反,我只是稍微简化一下方法实现:

        int countNeighbors(Cell c, int gen, boolean countLive) {
           int x = c.getX();
           int y = c.getY();
           int counter = 0;
           for (int i = x - 1; i <= x + 1; i++) {
              for (int j = y - 1; j <= y + 1; j++) {
                 if (i == x && j == y)
                    continue;
                 CellState s = getCell(i, j).getCellState(gen);
                 if ((countLive && (s == CellState.LIVE || s == CellState.SICK)) ||
                     (!countLive && (s == CellState.DEAD || s == CellState.DEAD4GOOD))) {
                    counter++;
                 }
              }
           }
           return counter;
        }
        

        【讨论】:

          【解决方案7】:

          在使用重构方面,您可以做的一些事情是;

          • 复制该方法并创建两个版本,一个是真正的硬编码,另一个是假的硬编码。您的重构工具应该可以帮助您内联此常量并根据需要删除代码。
          • 重新创建调用上述正确 true/false 方法的方法,以实现向后兼容性。然后,您可以内联此方法。

          【讨论】:

            【解决方案8】:

            我倾向于在这里保留 CellState 枚举中的映射以进行计数,然后根据需要添加 LIVE 和 SICK 或 DEAD 和 DEAD4GOOD。

            int calculateNumOfLiveOrDeadNeighbors(Cell c, int gen, boolean countLiveOnes) {
                final int x = c.getX();
                final int y = c.getY();
                final HashMap<CellState, Integer> counts = new HashMap<CellState, Integer>();
                for (CellState state : CellState.values())
                    counts.put(state, 0);
            
                for (int i = x - 1; i < x + 2; i++) {
                    for (int j = y - 1; j < y + 2; j++) {
                        if (i == x && j == y)
                            continue;
                        CellState state = getCell(i, j).getCellState(gen);
                        counts.put(state, counts.get(state) + 1);
                    }
                }
                if (countLiveOnes)
                    return counts.get(CellState.LIVE) + counts.get(CellState.SICK);
                else
                    return counts.get(CellState.DEAD) + counts.get(CellState.DEAD4GOOD);
            }
            

            【讨论】:

              【解决方案9】:

              拥有一个私有方法,它是您当前拥有的内容的精确复制和粘贴。 然后创建两个新方法,每个方法都有一个更具描述性的名称,只需使用适当的布尔值调用您的私有方法

              【讨论】:

                猜你喜欢
                • 2020-12-13
                • 1970-01-01
                • 1970-01-01
                • 2010-11-12
                • 2020-08-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-05-09
                相关资源
                最近更新 更多