3

从此代码开始:

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

如何将其重构为:

 new Person("ET", 88)

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

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

你会说流畅接口的一个缺点是它们不容易被重构吗?

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

4

5 回答 5

4

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

如果WithAge是单行代码,则可以将代码移至新的构造函数,并InitAge完全取消,除非使用附加方法提供额外的可读性。

拥有良好的单元测试将隔离引入错误的位置(如果有的话)。

于 2009-04-11T04:14:32.647 回答
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,则将其设为私有,或者如果您想同时允许两种方式,则将其公开。

于 2009-04-11T04:13:40.327 回答
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 中的重构功能要么设计得足够灵活,让您创造性地发明新的重构,要么针对某些常见情况进行硬编码。我更愿意将常见案例定义为我可以变异以发明新案例的示例。

于 2009-04-11T08:17:24.750 回答
1

我在这类事情上没有任何实际经验,但如果我遇到你的情况,我会去寻找自定义 Eclipse 重构(或 Refactor 中的等价物!Pro for .Net,如果你是这样的话使用)。

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

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

类人{
  公共人员(字符串名称,整数年龄);
  公共人员(字符串名称,int numberOfChildren);
}

无法判断对 Person.WithAge 的链式调用应该替换为哪些构造函数。

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

除此之外,它似乎很简单。给新的构造函数以下内容:

公共人员(字符串名称,整数年龄){
  这个(名字);
  与年龄(年龄);
}

然后,您可以安全地用新调用替换原来的调用。

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

于 2009-04-11T08:01:21.900 回答
0
  1. 为旧代码编写单元测试。

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

于 2009-04-11T04:11:13.640 回答