在对不同对象属性执行操作时避免重复代码

时间:2013-06-03 14:07:45

标签: algorithm oop design-patterns code-duplication

我最近遇到了一个让我思考的问题。假设我有一个O类型的对象,其属性为O.A和O.B.还假设我有一个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),但这是我目前的想法。

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

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

4 个答案:

答案 0 :(得分:2)

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

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

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. 从实施的角度来看,它可以防止重复,因为它在一个地方。这对开发有利。

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

问题可能会减少到从给定对象获取指定字段的值,因此可用于排序目的,或者

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)一样传递。

答案 3 :(得分:0)

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

这就是为什么你应该使用可用的工具,比如框架或其他提供所请求解决方案的代码库。

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

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

我假设您使用Java:

在你的情况下,已经亲自实施了Collection#sort(List, Comparator)

要完全填充它,您可以创建一个枚举类型,以实现具有预定义排序类型的Comparator接口。

相关问题