重构第12天 分解依赖(Break Dependencies)
理解:“分解依赖” 是指对部分不满足我们要求的类和方法进行依赖分解,通过装饰器来达到我们需要的功能。
详解:今天我们所讲的这个重构方法对于单元测试是非常有用的。如果你要在你的代码中加入单元测试但有一部分代码是你不想测试的,那么你应用使用这个的重构。
这个例子中我们客户端使用了一个静态类来完成部分工作,但是问题就在于进行单元测试的时候,我们无法模拟静态类进行测试。为了使单元测试能够进行,我们用一个接口来包装这个静态类来完成静态类的依赖分解。
重构前代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ReflectorDemo 7 { 8 public class AnimalFeedingService 9 { 10 11 private bool FoodBowlEmpty { get; set; } 12 13 public void Feed() 14 { 15 if (FoodBowlEmpty) 16 { 17 Feeder.ReplenishFood(); 18 } 19 // more code to feed the animal 20 } 21 } 22 23 public static class Feeder 24 { 25 26 public static void ReplenishFood() 27 { 28 // fill up bowl 29 } 30 } 31 }
我们添加一个接口和一个实现类,在实现类中调用静态类的方法,所以说具体做什么事情没有改变,改变的只是形式,但这样做的一个好处是增加了了代码的可测试性。在应用了分解依赖模式后,我们就可以在单元测试的时候mock一个IFeederService对象并通过AnimalFeedingService的构造函数传递给它。这样就可以完成我们需要的功能。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ReflectorDemo 7 { 8 public class AnimalFeedingService 9 { 10 11 public IFeederService FeederService { get; set; } 12 13 private bool FoodBowlEmpty { get; set; } 14 15 public AnimalFeedingService(IFeederService feederServices) 16 { 17 FeederService = feederServices; 18 } 19 20 public void Feed() 21 { 22 if (FoodBowlEmpty) 23 { 24 FeederService.ReplenishFood(); 25 } 26 // more code to feed the animal 27 } 28 } 29 30 public static class Feeder 31 { 32 33 public static void ReplenishFood() 34 { 35 // fill up bowl 36 } 37 } 38 public interface IFeederService 39 { 40 void ReplenishFood(); 41 } 42 43 44 public class FeederService : IFeederService 45 { 46 47 public void ReplenishFood() 48 { 49 50 Feeder.ReplenishFood(); 51 } 52 } 53 }
这样,我们在单元测试的时候就可以模拟(MOCK)一个IFeederService 接口的实例,通过AnimalFeedingService 的构造函数传进去。
这个重构在很多时候和设计模式中的一些思想类似,使用中间的装饰接口来分解两个类之间的依赖,对类进行装饰,然后使它满足我们所需要的功能。
每天早上敲醒自己的不是闹钟,是夢想!