1

我有一个巨大的类A,我想重构并提取一个具有某些行为的部分类。现在我不想A一步重构为 10 个部分类,但我想首先采取第一个行为,将其B重构为C.

然后A它作为一种main意志接受DI方式B并将C它们用作帮助者。

我的问题是这样的:

因为这是一个部分重构(连续的......)B并且C依赖于一些逻辑,A它不可能重构AB并且C没有B并且C依赖于A逻辑,否则我需要立即A分解为 10 或 20 个类。

我剩下的是:

  1. BC接受(丑陋)A方式DI虽然丑陋,但这将允许我进行连续重构事件更糟糕A的是尚未准备好尚未构造,直到我用助手调用它的构造函数BC.
  2. 做完整的重构——做不到!它太复杂太冒险了,我更喜欢一步一步来——所以它也是不可接受的!我需要在重构中采取小步骤并逐步构建重构。每次我接触任何代码时,我都会做一些清理和重构(遗留代码),不能一次重构整个事情。
  3. DI使用 setter 也不可接受,我更喜欢依赖于ctor.

对此有什么想法吗?有什么模式吗?

4

4 回答 4

2

通常这些是我用来进行重构的步骤/规则。执行这些步骤时,我通常不会发现任何问题。这些示例在 C# 中。

  1. 删除静态函数(可选)

    如果要删除静态函数,通常我将其包装为一个类并进行默认字段注入(in C#is property)。请注意,通常这是为无状态函数完成的。

    public static class StaticExample{
        public static void DoSomething(int a){ /*code here*/ }
    }
    public class WrapperExample{
        public void DoSomething(int a){ return StaticExample.DoSomething(a); }
    }
    public class Consumer{
        public WrapperExample wrapperExample = new WrapperExample();
        public void ConsumeBefore(int a){ StaticExample.DoSomething(a); }
        public void ConsumeAfter(int a){ wrapperExample.DoSomething(a); }
    }
    

    这导致对当前逻辑的影响为零,并为更多重构(依赖注入、修改 Wrapper 以不使用静态等)提供了良好的基线。

  2. 先重构端点

    通常一个函数会执行几个步骤。一些例子是:

    • 购物车结帐:获取购物车物品,验证购物车,验证付款,减去存储数量,将购物车标记为完成,[打印发票] <--这是端点

    • 回答stackoverflow问题:获取答案,验证答案,插入/标记答案为已发布,[发送通知] <--这是端点

    这通常更容易完成,因为:

    1. 通常在端点执行期间不需要外部资源(参数/变量/数据),
    2. 通常它不需要太多的数据操作,

    3. 通常它与外部依赖项(例如数据库或打印机)有交互,因此值得提取。

    第 1 点通常通过重构类解决您的循环依赖。

  3. 使其无状态。如果它是有状态的,则重构为参数

    假设你的一个班级有这个:

    public class Foo(
        public string Bar = "";
        public void DoSomething(){
            // the code you want to refactor using Bar
        }
    )
    

    变得

    public class Foo(
        public string Bar = "";
        public RefactoredClass refactoredClass = new RefactoredClass();
        public void DoSomething(){
            refactoredClass.DoSomething(Bar);
        }
    )
    
  4. 几个类似的参数变成了一个DTO

    此重构旨在简化过多的参数。通常这意味着参数集是否相互之间存在某种关系。例如(可能不是现实生活场景):

    string cartId, string productId, int quantity --> CartItem
    string cartId, string productId, int quantity, string userName, string invoiceNumber --> this is not right
    

    第一个示例可以重构为 DTO,因为参数仍然在相同的上下文中 ( CartItem)。在第二个示例中, and 的上下文与userNameandinvoiceNumber无关。productIdquantity

    它可能导致违规,SRP因为看起来该函数将处理 2 个不同的事情。但是,如果userNameinvoiceNumber嵌入到CartHeader具有 数组的 中CartItems,则它成为一个上下文,可能会打印发票。

这是我的 2 美分。我没有任何来源或参考资料。

于 2013-07-31T08:11:30.030 回答
1

由于您还没有真正说明您所说的那些依赖项来自哪里,我正在做一些猜测。

您说在逐步重构之后,类 B 和 C 将循环依赖回 A。听起来 A 中的逻辑是用一个大杂乱的方法编写的,做很多不同的事情,或者 A 的所有方法都非常依赖A 的状态不是很好(特别是如果你想进行单元测试)。也就是说,我建议尝试在具有一个责任或(理想情况下)不依赖于状态的方法中开始构建 A 的逻辑(例如,也许您应该通过参数传递状态)。

因此,在不创建循环依赖项的情况下,应该更容易将方法提取到辅助类中。

于 2013-07-30T09:52:46.037 回答
1

在官方重构提取类模式中找到了一个“官方”答案,该模式讨论了这个确切的问题:

http://sourcemaking.com/refactoring/extract-class

力学

06 决定如何划分班级的职责。创建一个新类来表达分离的职责。如果旧类的职责与其名称不再匹配,请重命名旧类。建立从旧课程到新课程的链接。 您可能需要双向链接。但是在你发现你需要它之​​前不要做反向链接。

所以有时除了建立这种双向链接之外别无他法。

于 2013-07-30T13:11:31.360 回答
-1

尝试中介者模式。A 和部分类之间的所有交互都应该封装在中介类中。

于 2013-07-30T09:42:35.360 回答