修改代码的艺术, part 1

      2年前在实习的时候买了这本书,当时就随便翻了翻,也没看懂太多;  最近比较轻松,碰巧比较关注代码质量,一口气就把这书读完了。 这本书虽然关注点比较窄(重构代码,解开依赖,方便测试),但在这方面就众多情形提出了各自的解决方案,不得不说作者富有相当的代码洞察能力和实践经验。总的来说是本好书。

      下面是我自己的一点体会:

PⅠ,概念和原则

      1.  测试有关

            测试代码并不难写, 它毕竟也只是代码, 胡乱一通总可以得到个结论, 但如果这样做, 也许我们还得需要额外的东西来测试我们的测试代码; 同样由于只是代码, 我们就有大量可以展现其漂亮的方式. 编写测试代码比较困难的一个因素就是测试对象内部与其外部的依赖.

            假设我们现在完成了一个典型的3层系统, 如果我们需要测试逻辑层是否正确, 正式产品的运作方式是需要向下访问数据层的, 这里多半需要提供数据库的连接, 但由于层次的明确, 如果我们测试时获取数据的方式同样采取访问产品的数据层, 那么一方面, 设置数据层环境并不那么容易, 我们需要付出额外的工作代价; 另一方面, 如果测试失败, 由于我们引入了另外的层次, 错误将难定位.

            测试就是给定语义上下文合理的输入, 执行测试方法, 比较实际输出和语义逻辑输出.

            方便的测试就是除去所有复杂无意义的依赖, 实例化测试类(产品类的去依赖版本), 运行其上复写的简易方法, 然后实际结果与意义上的逻辑结果比较.

    2. 本书有关

            接缝: 指程序中的一些特殊点, 在这些点上你无需作任何修改就可以达到改变程序行为的目的. 面象对象方面, 虚函数就是一种接缝.  

    3. 解决方案

            本书就是围绕这类主题, 其提出的各种解决方案本质都是寻找适当的接缝, 引入中间层(通常是接口), 以解除测试对象有关的各种依赖.

 

PⅡ, 具体技巧

      1. 接口提取

      原始代码: TargetFun是我们要测试的方法, 它依赖了DependenceObj的Fun函数.

 1 Class TargetCls
 2 ExpandedBlockStart.gifContractedBlock.gif {
 3    public void TargetFun()
 4ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 5           
 6
 7           DependenceObj obj = new DependenceObj();
 8           obj.Fun();
 9
10           
11        }

12}

      重构: TestFun是我们与TargetFun配套的测试方法, 其实现基本一样, 区别仅仅是IObj指向对象不同.

ContractedBlock.gif ExpandedBlockStart.gif Code
 1interface IObj
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    void Fun();
 4}

 5
 6class DependenceObj : IObj
 7ExpandedBlockStart.gifContractedBlock.gif{
 8    public void Fun()
 9ExpandedSubBlockStart.gifContractedSubBlock.gif    {
10            //Complicated things
11    }

12}

13
14class FakeObj : IObj
15ExpandedBlockStart.gifContractedBlock.gif{
16    public void Fun()
17ExpandedSubBlockStart.gifContractedSubBlock.gif    {
18           //Easy things
19    }

20}

21
22Class TargetCls
23ExpandedBlockStart.gifContractedBlock.gif{
24    public void TargetFun()
25ExpandedSubBlockStart.gifContractedSubBlock.gif        {
26           
27
28           IObj obj = new DependenceObj();
29           obj.Fun();
30
31           
32        }

33}

34
35Class TestCls
36ExpandedBlockStart.gifContractedBlock.gif{
37    public void TestFun()
38ExpandedSubBlockStart.gifContractedSubBlock.gif    {
39        
40
41        IObj obj = new FakeObj();
42        obj.Fun();
43
44        
45    }

46}

47
48

    这个例子存在代码重复, 下面的方法可以避免这个问题.

    2. 参数化

    原始代码:

 1 Class TargetCls
 2 ExpandedBlockStart.gifContractedBlock.gif {
 3    public void TargetFun()
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 5       
 6    
 7       DependenceObj obj = new DependenceObj();
 8       obj.Fun();
 9
10         
11    }

12}

      重构: 

ContractedBlock.gif ExpandedBlockStart.gif Code
 1interface IObj
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    void Fun();
 4}
 5
 6class DependanceObj : IObj
 7ExpandedBlockStart.gifContractedBlock.gif{
 8    public void Fun()
 9ExpandedSubBlockStart.gifContractedSubBlock.gif    {
10        
11    }

12}

13
14class FakeObj : IObj
15ExpandedBlockStart.gifContractedBlock.gif{
16    public void Fun()
17ExpandedSubBlockStart.gifContractedSubBlock.gif    {
18        
19    }

20}

21
22Class TargetCls
23ExpandedBlockStart.gifContractedBlock.gif{
24
25    public void TargetFun()
26ExpandedSubBlockStart.gifContractedSubBlock.gif    {
27        DelegateTargetFun(new DependanceObj());
28    }

29    
30    public void DelegateTargetFun(IObj obj)
31ExpandedSubBlockStart.gifContractedSubBlock.gif    {
32       
33   
34       obj.Fun();
35
36          
37    }

38}

39
40Class TestCls
41ExpandedBlockStart.gifContractedBlock.gif{
42    public void TestFun()
43ExpandedSubBlockStart.gifContractedSubBlock.gif    {
44        IObj fakeObj = new FakeObj();
45        TargetCls target = new TargetCls();
46        target.DelegateTargetFun(fakeObj);
47    }

48}

     对于由于依赖难以执行的方法都可以尝试此技巧. 比如某构造函数内部依赖严重难以创建此类型对象, 我们可以将依赖对象由外部传入, 测试时只需要传入简单对象即可. 为了保持一致性, 带参数方法多作为原方法委托实现.

    3. 子类化并重写

    原始代码: 

ContractedBlock.gif ExpandedBlockStart.gif Code
 1class TargetCls
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    public void TargetFun()
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 5        
 6
 7        DependanceFun();   
 8
 9        
10    }

11
12    private void DependanceFun()
13ExpandedSubBlockStart.gifContractedSubBlock.gif    {
14               //Complicated things
15    }

16}

    重构:

ContractedBlock.gif ExpandedBlockStart.gif Code
 1class TargetCls
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    public void TargetFun()
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 5        
 6
 7        DependanceFun();   
 8
 9        
10    }
11
12    protected virtual void DependanceFun()               
13ExpandedSubBlockStart.gifContractedSubBlock.gif    {
14               //Complicated things
15    }

16}

17
18class TestCls : TargetCls
19ExpandedBlockStart.gifContractedBlock.gif{
20    protected override void DependanceFun()
21ExpandedSubBlockStart.gifContractedSubBlock.gif    {
22               //Easy things
23    }

24}

25

    为了可以override依赖方法, 此方法必须声明virtual, 并提升访问权限.

    4. 方法分解, 子类化

    原始代码:

ContractedBlock.gif ExpandedBlockStart.gif Code
 1class TargetCls
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    public void TargetFun()
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 5        
 6               //Complicated part 1
 7               //Complicated part 2
 8        
 9               //Complicated part N
10        
11    }

12}

    重构: 

ContractedBlock.gif ExpandedBlockStart.gif Code
 1class TargetCls
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    public virtual void InnerPartOne()
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    {
 5        
 6    }

 7
 8    public virtual void InnerPartTwo()
 9ExpandedSubBlockStart.gifContractedSubBlock.gif    {
10        
11    }

12
13    public virtual void InnerPartN()
14ExpandedSubBlockStart.gifContractedSubBlock.gif    {
15        
16    }

17
18    public void TargetFun()
19ExpandedSubBlockStart.gifContractedSubBlock.gif    {
20        
21        InnerPartOne();       //Complicated part 1
22        InnerPartTwo();       //Complicated part 2
23        
24        InnerPartN();         //Complicated part N
25        
26    }

27}

28
29class TestCls : TargetCls
30ExpandedBlockStart.gifContractedBlock.gif{
31    public override void InnerPartOne()
32ExpandedSubBlockStart.gifContractedSubBlock.gif    {
33        
34    }

35
36    public override void InnerPartTwo()
37ExpandedSubBlockStart.gifContractedSubBlock.gif    {
38        
39    }

40
41
42    public override void InnerPartN()
43ExpandedSubBlockStart.gifContractedSubBlock.gif    {
44        
45    }

46
47}

    分解巨型方法是关键.

 5. 剥离并外覆

    子类化是一个重要的解依赖手段, 但有时候由于特殊原因,  被依赖的类不能被继承(sealed).

    原始代码:

 1 sealed   class  UninheritCls
 2 ExpandedBlockStart.gifContractedBlock.gif {
 3
 4}

 5
 6 class  TargetCls
 7 ExpandedBlockStart.gifContractedBlock.gif {
 8    public void TargetFun(UninheritCls obj)
 9ExpandedSubBlockStart.gifContractedSubBlock.gif    {
10        
11        obj.Fun();
12        
13    }

14}

     重构: 

ContractedBlock.gif ExpandedBlockStart.gif Code
 1sealed class UninheritCls
 2ExpandedBlockStart.gifContractedBlock.gif{
 3    public void Fun()
 4ExpandedSubBlockStart.gifContractedSubBlock.gif    {}
 5}

 6
 7interface IObj
 8ExpandedBlockStart.gifContractedBlock.gif{
 9    void Fun();
10}

11
12class UninheritWrapper : IObj
13ExpandedBlockStart.gifContractedBlock.gif{
14    private UninheritCls innerObj = new UninheritCls();
15
16    public UninheritWrapper(UninheritCls param)
17ExpandedSubBlockStart.gifContractedSubBlock.gif    {
18        innerObj = param;
19    }

20    
21    public void Fun()
22ExpandedSubBlockStart.gifContractedSubBlock.gif    
23       innerObj.Fun();
24    }

25}

26
27class FakeUninheritCls : IObj
28ExpandedBlockStart.gifContractedBlock.gif{
29    public void Fun()
30ExpandedSubBlockStart.gifContractedSubBlock.gif    {}
31}

32
33class TargetCls
34ExpandedBlockStart.gifContractedBlock.gif{
35    public void TargetFun(IObj obj)   //Uses UninheritWrapper object as parameter when at production, FakeUninheritCls object at testing
36ExpandedSubBlockStart.gifContractedSubBlock.gif    {
37        
38        obj.Fun();
39        
40    }

41}

42
43class TestCls
44ExpandedBlockStart.gifContractedBlock.gif{
45    public void TestFun()
46ExpandedSubBlockStart.gifContractedSubBlock.gif    {
47        IObj fakeObj = new FakeUninheritCls();
48        TargetCls target = new TargetCls();
49        target.TargetFun(fakeObj);
50    }

51}

 

转载于:https://www.cnblogs.com/Tyrale/archive/2009/06/23/1509516.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值