重构建议和工具

时间:2010-08-24 08:56:04

标签: java refactoring

我有一些代码由很多(几百个LOC)的uggly条件组成,即

SomeClass someClass = null;

if("foo".equals(fooBar)) {
    // do something possibly involving more if-else statments
    // and possibly modify the someClass variable among others...
} else if("bar".equals(fooBar)) {
    // Same as above but with some slight variations
} else if("baz".equals(fooBar)) {
    // and yet again as above
} 
    //... lots of more else ifs
} else  {
    // and if nothing matches it is probably an error... 
    // so there is some error handling here
}

// Some code that acts on someClass
GenerateOutput(someClass);

现在我有了重构这类代码的想法:

abstract class CheckPerform<S,T,Q> {
    private CheckPerform<T> next;
    CheckPerform(CheckPerform<T> next) {
        this.next = next;
    }

    protected abstract T perform(S arg);
    protected abstract boolean check(Q toCheck);

    public T checkPerform(S arg, Q toCheck) {
        if(check(toCheck)) {
            return perform(arg);
        } 

        // Check if this CheckPerform is the last in the chain...
        return next == null ? null : next.checkPerform();
    }
}

对于每个if语句生成CheckPerform的子类,例如

class CheckPerformFoo extends CheckPerform<SomeInput, SomeClass, String> {
    CheckPerformFoo(CheckPerform<SomeInput, SomeClass, String> next) {
        super(next);
    }

    protected boolean check(String toCheck) {
        // same check as in the if-statment with "foo" above"
        returs "foo".equals(toCheck);
    }

    protected SomeClass perform(SomeInput arg) {
        // Perform same actions (as in the "foo" if-statment) 
        // and return a SomeClass instance (that is in the 
        // same state as in the "foo" if-statment) 
    }
}

然后我可以将不同的CheckPerforms注入彼此,以便进行相同的检查顺序并采取相应的操作。在原始类中,我只需要注入一个CheckPerform对象。这是解决此类问题的有效方法吗?我项目中的类数可能会爆炸,但至少我将获得更多模块化和可测试的代码。我应该以其他方式这样做吗?

由于这些if-else-if -...- else-if-else语句是我所谓的代码库的重复主题,我想尽可能自动地进行重构。那么我可以用什么工具来自动化呢?

a)一些可自定义的重构功能隐藏在我错过的IDE中(最好是在Eclipse或IDEA中) b)一些外部工具,可以解析Java代码并给我细粒度的转换控制 c)我应该自己使用Scala破解它吗? d)我应该手动浏览每个类并使用我在IDE中熟悉的功能进行重构吗?

理想情况下,重构的输出还应该包括一些我可以运行的基本测试代码模板(最好也可以测试原始代码的案例,这些代码可以作为一种回归测试在新旧上运行......但是我稍后离开。)

感谢您的任何意见和建议!

3 个答案:

答案 0 :(得分:2)

你所描述的是Chain of Responsibility Pattern,这听起来对你的重构来说可能是一个不错的选择。可能会有一些缺点。

  • 可读性因为您将使用spring或其他类似注入CheckPerformers的顺序,这意味着很难看到代码在第一时间实际执行的操作。
  • 维护如果有人想要添加新条件,并添加一个全新的类,他们还必须编辑一些弹簧配置。选择正确的位置来添加新的CheckPerformer可能很困难且容易出错。
  • 许多课程根据您拥有的条件数量以及在这些条件下重复代码的数量,您最终可能会遇到很多新课程。即使长篇清单中的if也非常漂亮,它在一个地方的逻辑,这又有助于提高可读性。

答案 1 :(得分:1)

对我来说,最简单的方法是涉及Map<String, Action>,即将各种字符串映射到要执行的特定操作。这样,查找将比CheckPerform*类中的手动比较更简单,更高效,从而消除了大量重复的代码。

这些操作可以与您的设计类似地实现,作为公共接口的子类,但使用带有重写方法的枚举可能更容易,更紧凑。您可以在an earlier answer of mine中看到此示例。

不幸的是,我不知道任何可以帮助你的自动重构。早些时候,当我做了一些类似的重构时,我编写了单元测试,并使用Move Method等人的自动支持手动逐步进行重构。当然,由于单元测试在结构上彼此非常相似,我可以在那里重用部分代码。

更新

@Sebastien在他的评论中指出,我错过了更大的if块中可能的子ifs。人们确实可以使用地图层次来解决这个问题。但是,如果层次结构开始非常复杂且具有大量重复功能,则可以进一步改进实现DSL,将整个映射从代码移动到配置文件或DB中。最简单的形式可能看起来像

foo -> com.foo.bar.SomeClass.someMethod
    biz -> com.foo.bar.SomeOtherClass.someOtherMethod
    baz -> com.foo.bar.YetAnotherClass.someMethod
bar -> com.foo.bar.SomeOtherClass.someMethod
    biz -> com.foo.bar.DifferentClass.aMethod
    baz -> com.foo.bar.AndAnotherClass.anotherMethod

其中缩进行为每个更大的案例配置子条件。

答案 2 :(得分:1)

要回答问题的更一般部分,除了基本的IDE支持之外,我不知道任何自动重构的工具,但是如果你想知道重构的内容,请查看{{3} }。 Refactoring catalogreplace conditional with Polymorphism涵盖了您问题的具体内容。