代码之家  ›  专栏  ›  技术社区  ›  Bjorn Reppen

如何重构链接方法?

  •  3
  • Bjorn Reppen  · 技术社区  · 16 年前

    从以下代码开始:

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

    如何将其重构为:

     new Person("ET", 88)
    

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

    为什么?因为可能会有数百个这样的错误,我不想通过手工操作引入错误。

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

    注意:我想在不手动键入代码的情况下自动执行此操作。

    5 回复  |  直到 16 年前
        1
  •  4
  •   lc.    16 年前

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

    如果 WithAge 是一个一行程序,您只需将代码移到新的构造函数中,然后去掉 内含物 总之,除非有额外的方法提供额外的可读性。

    如果有好的单元测试,将隔离引入错误的地方。

        2
  •  3
  •   Davy8    16 年前

    假设withage是返回一个人的方法,那么

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

    或更广义:

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

    然后,如果你不想让FluentMethod私有化,或者如果你想允许两种方式,就让它公开化。

        3
  •  2
  •   Daniel Earwicker    16 年前

    如果这是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。通过简单的文本搜索和替换,再加上一个可以重放一系列按键的宏,您可以获得很长的解决方案。

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

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

        4
  •  1
  •   Morendil    16 年前

    我对这类事情没有任何实际经验,但如果我处在你的情况下,我会去寻找的地方是 custom Eclipse refactorings (或重构中的等效项)!如果你使用的是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,即在部分构造的对象上,与在构造函数之后调用它不同。如果你有一个继承链,那么差异就很重要。 如果随着年龄的增长做了一些不平凡的事情。但这就是你的单元测试的目的……)

        5
  •  0
  •   Chris Ballance    16 年前
    1. 为旧代码编写单元测试。

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