【问题标题】:Avoiding duplicate code when performing operation on different object properties对不同对象属性执行操作时避免重复代码
【发布时间】:2013-06-03 14:07:45
【问题描述】:

我最近遇到了一个问题,让我一直在思考。假设我有一个具有 O.A 和 O.B 属性的 O 类型对象。还假设我有一组 O 类型的实例,其中为每个实例定义了 O.A 和 O.B。

现在假设我需要使用 O.A 或 O.B 对 O 实例的集合执行一些操作(如排序),但不能在任何给定时间同时使用。我原来的解决方案如下。

示例——仅用于演示,而非生产代码:

public class O {
    int A;
    int B;
}

public static class Utils {
    public static void SortByA (O[] collection) {
        // Sort the objects in the collection using O.A as the key. Note: this is custom sorting logic, so it is not simply a one-line call to a built-in sort method.
    }

    public static void SortByB (O[] collection) {
        // Sort the objects in the collection using O.B as the key. Same logic as above.
    }
}

我最想做的是……

public static void SortAgnostic (O[] collection, FieldRepresentation x /* some non-bool, non-int variable representing whether to chose O.A or O.B as the sorting key */) {
    // Sort by whatever "x" represents...
}

...但是创建一个新的、高度特定的类型,我必须维护它以避免重复几行代码似乎对我来说是不必要的。也许我在这方面是不正确的(如果该陈述是错误的,我相信有人会纠正我:D),但这仍然是我目前的想法。

问题:实现此方法的最佳方法是什么?我必须实现的逻辑很难分解成更小的方法,因为它已经相当优化了。问题的根源在于我需要使用对象的不同属性执行相同的操作。我想远离使用代码/标志/等。在方法签名中如果可能,以便解决方案尽可能稳健。

注意:在回答这个问题时,请从算法的角度出发。我知道某些特定于语言的功能可能是合适的替代方案,但我之前遇到过这个问题,并想从相对与语言无关的角度来理解它。另外,请不要仅将响应限制为排序解决方案,因为我仅选择它作为示例。真正的问题是在对对象的两个不同属性执行相同操作时如何避免代码重复。

【问题讨论】:

  • 我觉得您的问题更多地与特定于语言的功能有关。我不明白“算法的观点”。唯一的逻辑发生在您的排序方法中。如果您不想添加标志来选择要使用的属性,您可以使用匿名内部类(在 java 的情况下)或内联函数(在 Javascript 的情况下)让您的排序方法是不可知的。
  • @seeta -- 感谢您的评论。通过“算法的观点”,我只是意味着我想在我正在工作的几个项目中解决这个问题(例如带有 jquery、c#、java 等的 javascript)。每个都有不同的功能可以提供帮助,但我想知道“最小公分母解决方案”可以应用于最多数量的语言而不会发生重大变化。
  • 基本上你似乎想要一个Comparator(至少对于Java)。或者你想使用反射(这是相当特定于语言的)。

标签: algorithm oop design-patterns code-duplication


【解决方案1】:

“真正的问题是如何在对对象的两个不同属性执行相同操作时避免代码重复。”

这是一个非常好的问题,因为这种情况一直都在出现。我认为,处理这种情况的最佳方法之一是使用以下模式。

public class O {
    int A;
    int B;
}

public doOperationX1() {
   doOperationX(something to indicate which property to use);
}

public doOperationX2() {
   doOperationX(something to indicate which property to use);
}
private doOperationX(input ) {
     // actual work is done here
}

在这种模式中,实际的实现是在一个私有方法中执行的,该方法由公共方法调用,并带有一些额外的信息。例如,在这种情况下,它可以是 doOperationX(A) 或 doOperationX(B) 或类似的东西。

我的推理:我认为这种模式是最佳的,因为它实现了两个主要要求:

  1. 它使公共界面保持描述性和清晰性,因为它使操作分开,并避免了您在帖子中也提到的标志等。这对客户有好处。

  2. 从实施的角度来看,它可以防止重复,因为它在一个地方。这有利于发展。

【讨论】:

    【解决方案2】:

    我认为解决此问题的一种简单方法是将选择排序字段的行为内化到类O 本身。这样,解决方案可以与语言无关。

    Java 中的实现可以使用O 的抽象类,其中抽象方法getSortField() 的目的是返回要排序的字段。调用逻辑所需要做的就是实现抽象方法以返回所需的字段。

    O o = new O() {
        public int getSortField() {
            return A;
        }
    };
    

    【讨论】:

      【解决方案3】:

      问题可能会简化为从给定对象中获取指定字段的值,以便将其用于排序目的,或者,

      TField getValue(TEntity entity, string fieldName)
      {
          // Return value of field "A" from entity,
          // implementation depends on language of choice, possibly with 
          // some sort of reflection support
      }
      

      此方法可用于替代排序算法中的比较,

      if (getValue(o[i], "A")) > getValue(o[j], "A"))
      {
          swap(i, j);
      }
      

      然后可以将字段名称参数化为,

      public static void SortAgnostic (O[] collection, string fieldName) 
      {
          if (getValue(collection[i], fieldName)) > getValue(collection[j], fieldName))
          {
              swap(i, j);
          }
      
          ...
      }
      

      你可以像SortAgnostic(collection, "A")一样使用它。

      某些语言允许您以更优雅的方式表达该领域,

      public static void SortAgnostic (O[] collection, Expression fieldExpression) 
      {
          if (getValue(collection[i], fieldExpression)) > 
              getValue(collection[j], fieldExpression))
          {
              swap(i, j);
          }
      
          ...
      }
      

      你可以像SortAgnostic(collection, entity => entity.A)一样使用它。

      另外一种选择是传递一个指向函数的指针,该函数将返回所需字段的值,

      public static void SortAgnostic (O[] collection, Function getValue) 
      {
          if (getValue(collection[i])) > getValue(collection[j]))
          {
              swap(i, j);
          }
          ...
      }
      

      给定一个函数,

      TField getValueOfA(TEntity entity)
      {
          return entity.A;
      }
      

      然后像 SortAgnostic(collection, getValueOfA) 一样传递它。

      【讨论】:

        【解决方案4】:

        “...但是创建一个新的、高度特定的类型,我必须维护它以避免重复几行代码,这对我来说似乎没有必要”

        这就是为什么您应该使用可用的工具,例如框架或其他错误的代码库,为您提供所需的解决方案。

        当某些机制很常见时,这意味着它可以移动到更高的抽象级别。当您找不到合适的解决方案时,请尝试创建自己的解决方案。将操作结果视为不属于类功能的一部分。排序只是一个功能,这就是为什么它不应该从一开始就成为你课程的一部分。尽量保持课堂简单。

        不要因为小而过早地担心拥有小东西的感觉。专注于它的最终用途。如果您经常使用一种类型的排序,只需创建它的定义以重用它。您不必创建一个 utill 类然后调用它。有时,包含在 utill 类中的基本功能足够公平。

        我假设你使用 Java:

        在您的情况下,风团已经由Collection#sort(List, Comparator) 亲自实施。

        要完全填充它,您可以创建一个 Enum 类型,该类型使用预定义的排序类型实现 Comparator 接口。

        【讨论】:

          猜你喜欢
          • 2023-03-22
          • 1970-01-01
          • 1970-01-01
          • 2022-01-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-17
          • 1970-01-01
          相关资源
          最近更新 更多