美文网首页
遗留系统改造-修改代码的核心原则

遗留系统改造-修改代码的核心原则

作者: JayWu的架构笔记 | 来源:发表于2019-07-25 16:03 被阅读0次

前言

修改代码有非常多好的技巧,我们往往会被五花八门的方法所淹没,导致在正真需要的时候想不起来,或者在选择的时候无从下手。
因为我们缺少了一个指导原则。
在深入了解各种使用技巧前,我们必须先掌握修改的原则,才能更好地掌握技巧。

核心原则

修改代码的核心原则就是:为了使代码更好地测试。
具体到细节,会有两个主要的手段来保证原则的落地:感知和分离。

感知,即感知运行效果或者影响。
分离,即分离其他部分独立运行。

原则和手段都非常简单,但如果能够严格遵循,我们不仅仅可以写出更好测试的代码,同时还能写出高质量的代码。

接下来,我们会通过几个简单的案例,详细讲解如何运用原则,对代码进行修改和测试。

感知

下面是一个非常典型的场景,面对这种把内部逻辑包裹得严严实实的方法,很多时候我们是不是就放弃了治疗(测试)?或者直接跑跑页面看看结果?

public class Demo {
    public void scan(String x) {
        String result = x + x;
        // 需要将结果输出到某个地方
        System.out.println(result);
    }
}

究竟应该怎么样才能在测试中感知隐藏其中的result?
我们需要一个合作者来帮忙。

伪装成合作者

伪装成合作者的关键思路为,用新接口取代旧代码进行测试。
让我们来尝试一番。
修改代码
首先创建显示接口。

public interface Display {
    void show(String result);
}

然后实现原有显示功能。

public class ConsoleDisplay implements Display {
    public void show(String result) {
        System.out.println(result);
    }
}

用新接口取代旧代码。

public class Demo {
    private Display display;

    public Demo(Display display) {
        this.display = display;
    }

    public void scan(String x) {
        String result = x + x;
        display.show(result);
    }
}

测试代码
首先创建伪对象,最核心的地方在于暴露感知点。

public class FakeDisplay implements Display {

    private String result;

    public void show(String result) {
        // 捕获感知
        this.result = result;
    }

    // 暴露感知
    public String getResult() {
        return result;
    }
}

最后使用伪对象进行测试。

@Test
public void testSuccessfulResult() {
    FakeDisplay display = new FakeDisplay();
    String x = "1";
    new Demo(display).scan(x);
    Assert.assertEquals(x + x, display.getResult());
}

至此,我们不仅把代码变得可测试,同时还抽象出了一个显示接口,正好吻合了职责分离的设计原则,同时为未来输出到不同的设备做好了扩展的准备。

分离

还是刚刚那个例子,我们发现在显示逻辑后面,紧接着一个异常复杂的方法调用。
Invoker类里面的逻辑不仅仅庞大难懂,更糟糕的是里面充满了错综复杂的依赖,吓得你赶紧关了这个类,看半小时朋友圈压压惊,回来都不愿意看第二眼。

public class Demo {
    public void scan(String x) {
        String result = x + x;
        display.show(result);
        // 这里是一个非常复杂的方法
        new Invoker().invoke();
    }
}

但不幸的是,这个方法隐藏在需要测试的scan方法中,但我们只想静静地测试result,完全不想关心invoke的变幻莫测。
该怎么办呢?
我们需要找一个缝隙,把它彻底隔离掉。

对象接缝

这个方法的关键思路在于:将接缝对象以入参形式传入。
修改代码

public void scan(Invoker invoker, String x) {
    String result = x + x;
    display.show(result);
    invoker.invoke();
}

测试代码
首先创建伪对象,分离不关心逻辑。

public class FakeInvoker extends Invoker {

    @Override
    public void invoke() {
    }
}

最后用使用伪对象分离测试。

@Test
public void testSuccessfulResultWithoutInvoker() {
    FakeDisplay display = new FakeDisplay();
    String x = "1";
    new DemoV2(display).scan(new FakeInvoker(), x);
    Assert.assertEquals(x + x, display.getResult());
}

或许你会问,如果我不想改变原来的方法签名怎么办,因为很多地方都调用它。
针对这种情况,我们可以保持原有签名,创建默认对象即可。

public void scan(String x) {
    scan(new Inovker(), s);
}

方法接缝

除了常见的对象接缝,我们还会遇到一些奇怪的方法,比如下面的x(),你又得看半小时朋友圈来压压惊。

public class Demo {
    public void scan(String x) {
        String result = x + x;
        display.show(result);
            x();
    }
    private static void x() {
        // 这里是一个非常复杂的方法
    }
}

我们如何能够安静地测试result,同时去除静态方法的依赖呢?
修改代码
静态方法改为成员方法,使用protected可重写。

public void scan(String x) {
    String result = x + x;
    display.show(result);
    x();
}

protected void x() {
    // ...
}

测试代码
重写方法进行分离测试。

@Test
public void testSuccessfulResultWithoutX() {
    FakeDisplay display = new FakeDisplay();
    String x = "1";
    new DemoV2(display) {
        @Override
        protected void x() {
        }
    }.scan(x);
    Assert.assertEquals(x + x, display.getResult());
}

我们发现,分离原则能够更好地指导我们解除各种复杂的依赖。

总结

通过几个简单的案例,我们已经可以感受到修改原则的威力:
在不改变原有行为的基础上,进行安全地重构,得到可测试的代码,以及具有良好设计的代码。

本文是改造系列终章吗?

当然不是,我们才刚刚开始改造的步伐,前路漫漫,且看后续需要面临的荆棘:

  • 如何安全地修改原有方法
  • 如何添加新特性
  • 如何调整为可测试的代码
  • 修改时应该测试哪些方法
  • 修改时应该怎样写测试
  • 如何测试错综复杂的API调用
  • 如何快速理解代码
  • 如何修改大量相同的代码
  • 如何修改超级大类
  • 如何修改超长方法
  • ……

不管道路如何坎坷,感知和分离原则,一直是我们坚实地后盾,将伴随、指导遗留系统改造系列所有文章。

相关文章

  • 遗留系统改造-修改代码的核心原则

    前言 修改代码有非常多好的技巧,我们往往会被五花八门的方法所淹没,导致在正真需要的时候想不起来,或者在选择的时候无...

  • 遗留系统改造-如何安全地修改原有代码

    一个故事 在进入这个话题前,我们先讲一个故事。 开发同学从另一个团队接手了新的系统有一段时间了,但是平时都是加全新...

  • 遗留系统改造-开篇

    遗留代码 从何而来 软件是如何演变成遗留代码? 初期,架构清晰,代码精炼,指点江山 中期,新功能简单在原有代码加上...

  • 遗留系统改造-理解代码并编写测试

    前言 当我们开发一个新功能的时候,也曾经有过深入了解遗留系统的冲动,但阅读那些错综复杂的旧代码让人感觉头痛不已——...

  • 遗留系统的服务拆分

    最近一年来,我所在的项目为一个传统行业客户的 IT 核心系统做遗留系统改造[https://insights.th...

  • 39.面对遗留系统,你应该怎样做

    定义什么是遗留系统? 不经过充分测试的代码或者系统,称为遗留系统。 background: 在《你的代码是怎么变混...

  • 遗留系统改造策略选择

    什么是遗留系统? 遗留释义: (以前的事物或现象)继续存在;(过去)留下来:解决~问题。许多历史遗迹一直~到现在。...

  • 使用CDC模式改造遗留系统

    项目改造背景及挑战 在我们经历的各种遗留系统改造之旅中,使用绞杀者模式[https://martinfowler....

  • 软件泥潭真体验

    众所周知,改造遗留系统并非易事,如果该系统没有良好的架构和编码,那么在这基础上做功能升级改造,往往比做全新系统更加...

  • 遗留系统开发之道

    遗留系统之痛 问题 在软件这个行业里,有一个有意思的名词叫“祖传代码”。泛指那些结构混乱的遗留系统代码。相信大家或...

网友评论

      本文标题:遗留系统改造-修改代码的核心原则

      本文链接:https://www.haomeiwen.com/subject/bnuhrctx.html