我如何重构链式方法?

时间:2009-04-11 04:07:02

标签: refactoring fluent-interface

从这段代码开始:

 new Person("ET").WithAge(88)

如何重构:

 new Person("ET", 88)

需要执行哪些重构序列才能完成转换?

为什么呢?因为可能有数百个,我不想通过手动操作来引入错误。

你会说流利界面的缺点是它们不容易被重构吗?

注意:我想自动执行此操作而无需手动输入代码。

5 个答案:

答案 0 :(得分:4)

也许最简单的重构方法是将名称“WithAge”更改为“InitAge”,将InitAge设为私有,然后从构造函数中调用它。然后更新new Person(string).WithAge(int)的所有引用以使用新的构造函数。

如果WithAge是一行代码,您只需将代码移动到新构造函数中,然后完全取消InitAge,除非使用其他方法提供额外的可读性。

进行良好的单元测试将隔离引入错误的位置,如果是的话。

答案 1 :(得分:3)

假设WithAge是Person上返回Person的方法,那么

之类的东西
Person(string name, int age)
{
    this.name = name;
    this.WithAge(age);
}

或者更一般化:

Person(SomeType originalParameter, FluentParamType fluentParameter)
{
    //Original constructor stuff
    this.FluentMethod(fluentParameter);
}

然后如果您不想要它,请将FluentMethod设为私有,或者如果您想允许两种方式保持公开。

答案 2 :(得分:2)

如果这是C#(理想情况下你会用语言标记问题),Person类需要这个构造函数:

public Person(string name, int age)
    : this(name) { WithAge(age); }

然后在适当的时候更改所有客户端代码以调用此新构造函数,您需要找到所有出现的模式:

new Person(x1).WithAge(x2)

其中x1和x2是表达式,并将其替换为:

new Person(x1, x2)

如果除了WithAge之外还有其他修饰方法,它可能会变得更复杂。例如:

new Person(x1).WithHair(x2).WithAge(x3)

也许你想要成为:

new Person(x1, x3).WithHair(x2)

这完全取决于您是否有一个IDE,可以让您定义类似语言的搜索/替换模式。通过简单的文本搜索和替换,您可以获得很长的解决方案,并结合重放一系列按键的宏。

  

你会说流利的缺点吗?   接口是他们不容易的   重构?

不是特别 - IDE中的重构功能要么设计得足够灵活,要么能够创造性地发明新的重构,要么它们在某些常见情况下都是硬编码的。我更喜欢将常见案例定义为我可以改变以发明新案例的例子。

答案 3 :(得分:1)

我对这种事情没有任何实际经验,但是如果我处于你的情况,那么我要去的地方将是custom Eclipse refactorings(或等同于Refactor!Pro for .Net if那是你正在使用的。)

基本上你想要的是匹配和替换,除了你的正则表达式应该匹配抽象语法树而不是纯文本。这就是自动重构的原因。

此重构的一个风险是目标版本不如原始版本精确。考虑:

class Person {
  public Person(String name, int age);
  public Person(String name, int numberOfChildren);
}

没有办法告诉哪个构造函数对Person.WithAge的链接调用应替换为。

因此,在允许您继续之前,对此的自动支持必须检查这种歧义。如果已经有一个带有目标参数的构造函数,则中止重构。

除此之外,它看起来非常简单。为新构造函数提供以下内容:

public Person(String name, int age) {
  this(name);
  withAge(age);
}

然后您可以安全地用新的。

替换原始呼叫

(还有一个微妙的额外风险,因为在构造函数中调用withAge,即在部分构造的对象上,与在构造函数之后调用它不完全相同。如果你有一个继承链和如果withAge做了一件非常重要的事情。但那就是你的单元测试的目的......)

答案 4 :(得分:0)

  1. 为旧代码编写单元测试。

  2. 重新测试,直到测试再次通过。