DRY:尽量减少Java中重复的代码

时间:2011-01-26 21:41:01

标签: java dry

我正在用Java编写一个方法:

List<Foo> computeFooList(/* arguments */)
{
    /* snip */
}

我想用完全相同的逻辑编写第二种方法,但返回类型不同:

List<String> computeStringList(/* same arguments */)
{
    /* snip */
}

我正试图找出一种非hackish方法来最小化两种方法之间重复代码的数量。两者之间的唯一逻辑差异在于,当将对象添加到返回的列表时,第一个方法会添加实际Foo

List<Foo> computeFooList(/* arguments */)
{
    List<Foo> toReturn = ...
    ...
    for (Foo foo : /* some other list of Foo */)
    {
        if (/* some condition */)
        {
            toReturn.add(foo);
        }
    }
    ...
    return toReturn;
}

,第二个添加String的{​​{1}}表示形式:

Foo

实际上, 简单。我不想向List<String> computeStringList(/* same arguments */) { List<String> toReturn = ... ... for (Foo foo : /* some other list of Foo */) { if (/* some condition */) { toReturn.add(foo.toString()); } } ... } 添加Foo,除非我完全确定它属于那里。因此,使用辅助函数每个toReturn做出该决定。有两个不同版本的方法,我也需要不同版本的辅助函数 - 最后,我将编写两组近似相同的方法,但是对于一个小的泛型类型。


我可以编写一个包含所有决策逻辑的方法,但可以生成 fooList<Foo> 吗?是否可以在不使用原始List<String>类型(泛型土地中的不良做法!)或通配符List类型的情况下执行此操作?我想象一个看起来像这样的实现:

List<?>

这有什么好的,优雅的方法吗?我一直在玩泛型方法,但我看不到这样做的方法。即使用反射捣乱也没有把我带到任何地方(也许我需要TypeToken之类的东西?...... eww)。

3 个答案:

答案 0 :(得分:7)

您不能将转换逻辑外部化为单独的策略(例如Guava的Function<F, T>):

public <T> List<T> computeList(/* arguments */, Function<? super Foo, T> f) {
     List<T> toReturn = ...     ...     
     for (Foo foo : /* some other list of Foo */) {
         if (/* some condition */) {
             toReturn.add(f.apply(foo));
         }
     }
     return toReturn;
} 

computeFooList:

computeList(..., Functions.identity());

computeStringList:

computeList(..., Functions.toStringFunction());

答案 1 :(得分:0)

我有一个“SearchFilter”接口和一个“FilterAdapter”抽象类,我以类似的方式使用它。决策逻辑可以独立地并以通用方式从实际添加到返回列表中。我会检查每个Foo并说“true”包含它或“false”排除它。

public interface SearchFilter<T>
{
    public boolean include(T item);
    public Collection<T> apply(Collection<T> items);
}

可以使用apply()方法将过滤器应用于现有集合,并返回仅包含所需项目的新集合。

newCollection = myfilter.apply(originalItems);

这可能对您没有用,但include()概念应该可以很好地避免重复决策逻辑。

您可以拥有FooFilter extends FilterAdapter<Foo>(我也会在线匿名实例化这些内容),它提供了include的实现

public FooFilter extends FilterAdapter<Foo>
{
    public boolean include(Foo item)
    {
        if (item.isInvalid()) return false;
        // or any other complex logic you want
        return item.hasGoodThings();
    }
}

apply()方法几乎总是循环遍历初始集合并测试include,因此它在我的FilterAdapter中有一个默认实现,但可以被覆盖。

答案 2 :(得分:0)

这有点难看,但我认为这可能有用:

List<Foo> computeFooList(/* args */) {
    return computeEitherList(/* args */, Foo.class);
}

List<String> computeStringList(/* args */) {
    return computeEitherList(/* args */, String.class);
}

private <T> List<T> computeEitherList(/* args */, Class<T> whichType) {
    List<T> rval = new ArrayList<T>();
    for (Foo foo : listOfFoo) {
        // process foo

        if (whichType.equals(Foo.class)) {
            rval.add(whichType.cast(foo));
        }
        else if (whichType.equals(String.class)) {
            rval.add(whichType.cast(foo.toString()));
        }
        else {
            throw new SomeException("Cannot compute list for type " + whichType);
        }
    }
    return rval;
}