重构为部分类和循环依赖

时间:2013-07-30 09:32:02

标签: java oop design-patterns refactoring

我有一个巨大的类A,我想重构它并提取一个具有某些行为的分部类。现在我不想在一步中将A重构为10个部分类,但我想首先采取第一个行为,将其重构为B,然后在下次触及此类时采用连续重构方式另一种行为将其重构为C

然后A作为一种main将接受DI方式BC并将其用作帮助者。

我的问题是:

由于这是一个部分重构(连续......)BC依赖于A中的某些逻辑,因此无法将A重构为B {}}和C没有BC依赖于A逻辑,否则我需要立即将A分解为10或20个类。< / p>

我留下的是:

  1. BCA的方式接受(丑陋的)DI,虽然丑陋但这会让我继续重构事件更糟A是尚未准备好,直到我用帮助者BC调用其构造函数时才构建。
  2. 做完整的重构 - 做不到!它太复杂太冒险了我宁愿一步一步走 - 所以它也是不可接受的!我需要在重构中使用小步骤并逐步构建重构。每次我触摸任何代码我做一些清理和重构(遗留代码),不能一次重构整个事情。
  3. 带有setter的
  4. DI也是不可接受的,它是一个混乱,我更喜欢ctor中的依赖项。
  5. 有关于此的任何想法吗?任何模式?

4 个答案:

答案 0 :(得分:2)

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

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

    如果你想删除静态函数,通常我将它包装为一个类并进行默认字段注入(在C#属性中)。请注意,这通常用于无状态功能。

    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. 首先重构终点

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

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

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

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

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

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

    4. 第1点通常用重构类来解决循环依赖。

    5. 让它无国籍。如果它有状态,重构参数

      说你的一个班级有这个:

      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);
          }
      )
      
    6. 几个类似的参数成为DTO

      这个重构旨在简化太多参数。通常这意味着如果参数集彼此具有某种关系。例如(可能不是现实生活场景):

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

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

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

      < / LI>

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

答案 1 :(得分:1)

因为你还没有真正说明你所说的那些依赖关系来自我正在做一些猜测。

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

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

答案 2 :(得分:1)

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

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

  

力学

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

所以有时候没有办法解决这个双向联系问题。

答案 3 :(得分:-1)

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