3

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

现在假设我需要使用 OA 或 OB 对 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

4 回答 4

3

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

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

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

于 2013-06-03T17:51:05.330 回答
0

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

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

O o = new O() {
    public int getSortField() {
        return A;
    }
};
于 2013-06-03T15:14:55.443 回答
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);
    }

    ...
}

您可以使用 like SortAgnostic(collection, "A")

有些语言可以让你以更优雅的方式表达领域,

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

    ...
}

您可以使用 like 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)

于 2013-06-03T18:02:11.257 回答
0

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

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

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

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

我假设您使用 Java:

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

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

于 2013-06-03T23:55:33.200 回答