如何重构链式方法?

发布于 2024-07-17 08:03:43 字数 271 浏览 10 评论 0原文

从这段代码开始:

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

如何将其重构为:

 new Person("ET", 88)

需要执行什么重构顺序才能完成转换?

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

您是否认为流畅界面的缺点是它们不能轻松重构?

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

Starting with this code:

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

How can it be refactored to:

 new Person("ET", 88)

What sequence of refactorings needs to be performed to complete the transformation?

Why? Because there could be hundreds of these, and I wouldn't want to introduce errors by doing it manually.

Would you say a drawback with fluent interfaces is they can't easily be refactored?

NOTE: I want to do this automatically without hand typing the code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

南渊 2024-07-24 08:03:43

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

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

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

Perhaps the simplest way to refactor this is to change the name "WithAge" to "InitAge", make InitAge private, then call it from your constructor instead. Then update all references of new Person(string).WithAge(int) to use the new constructor.

If WithAge is a one-liner, you can just move the code to your new constructor instead, and do away with InitAge altogether, unless having the additional method provides extra readability.

Having good unit tests will isolate where errors are introduced, if they are.

怼怹恏 2024-07-24 08:03:43

方法怎么样

Person(string name, int age)
{
    this.name = name;
    this.WithAge(age);
}

假设 WithAge 是 Person 上的一个返回 Person 的方法,那么像或更一般化的

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

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

Assuming that WithAge is a method on Person that returns a Person, what about something like

Person(string name, int age)
{
    this.name = name;
    this.WithAge(age);
}

Or more generalized:

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

And then as make the FluentMethod private if you don't want it, or keep it public if you want to allow both ways.

蓝梦月影 2024-07-24 08:03:43

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

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

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

new Person(x1).WithAge(x2)

where x1 x2 是表达式,将其替换为:

new Person(x1, x2)

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

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

也许您希望它变成:

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

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

你能说一下流利的缺点吗?
接口是他们不容易
重构了吗?

并不是特别重要 - IDE 中的重构功能要么设计得足够灵活,可以让您创造性地发明新的重构,要么针对某些常见情况进行硬编码。 我更喜欢将常见案例定义为我可以改变以发明新案例的示例。

If this is C# (ideally you would tag the question with the language), the Person class needs this constructor:

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

To then change all client code to call this new constructor where appropriate, you would need to find all occurrences of the pattern:

new Person(x1).WithAge(x2)

where x1 and x2 are expressions, and replace them with:

new Person(x1, x2)

If there are other modifier methods aside from WithAge, it might get more complicated. For example:

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

Perhaps you'd want that to become:

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

It all depends on whether you have an IDE that lets you define language-aware search/replace patterns like that. You can get a long way to the solution with simple textual search and replace, combined with a macro that replays a sequence of key presses.

Would you say a drawback with fluent
interfaces is they can't easily be
refactored?

Not especially - it's more that refactoring features in IDEs are either designed flexibly enough to let you creatively invent new refactorings, or else they are hard-coded for certain common cases. I'd prefer the common cases to be defined as examples that I could mutate to invent new ones.

无声无音无过去 2024-07-24 08:03:43

我对此类事情没有任何实际经验,但如果我处于您的情况,我会去寻找的地方 自定义 Eclipse 重构(或者 Refactor! Pro for .Net 中的同等内容,如果您是这样的话)使用)。

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

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

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 与在构造函数之后调用它并不完全相同。如果您有继承链和<,则差异很重要/em> 如果 withAge 做了一些不平凡的事情,但这就是你的单元测试的目的......)

I don't have any practical experience with that sort of thing, but if I was in your situation the place I'd go looking would be custom Eclipse refactorings (or the equivalent in Refactor! Pro for .Net if that's what you're using).

Basically what you want is a match and replace, except that your regular expressions should match abstract syntax trees rather than plain text. That's what automated refactorings are.

One risk of this refactoring is that the target version is less precise than the original. Consider:

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

There is no way to tell which of these constructors the chained call to Person.WithAge should be replaced with.

So, automated support for this would have to check for such ambiguities before allowing you to proceed. If there is already a constructor with the target parameters, abort the refactoring.

Other than that it seems pretty straightforward. Give the new constructor the following content:

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

Then you can safely replace the original call with the new.

(There is a subtle additional risk, in that calling withAge within the constructor, i.e. on a partially constructed object, isn't quite the same as calling it after the constructor. The difference matters if you have an inheritance chain and if withAge does something non-trivial. But then that's what your unit tests are for...)

暖风昔人 2024-07-24 08:03:43
  1. 为旧代码编写单元测试。

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

  1. Write unit tests for the old code.

  2. Refactor until the tests pass again.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文