[转载]改名这件小事,你做对了吗?

2019-05-31 0 条评论 59 次阅读 7 人点赞

前面的文章里,我们讨论了命名的重要性,并为读者介绍了一些常见的命名原则。对于不能准确表达意图的神秘命名,解决的办法很简单:给它改个好名字。但是简单不等于容易,比如下面就是一个不那么容易的例子。

不谨慎的改名

一个再普通不过的星期四下午,小Y和他的队友们正在做代码评审。小Y是团队中的一名程序员,他对自己的代码水平还是很自信的,不过毕竟年轻,有时对代码细节想得还不是太深入。

“小Y呀,我觉得这个地方还可以再斟酌斟酌。”大Z指着屏幕说道。大Z是这个团队的Tech Lead,技术和沟通都很厉害,就是经常忙得没时间写代码。听他开口,大家都聚精会神地看着他指向的那段代码:

class Order {
    public double getPDA() {
    return isPremium ? totalAmount * 0.15 : 0;
}

“你看这个函数啊……我看看下面的实现代码呢,倒是能明白,这是在计算高级会员折扣金额,可要是光看函数名,这谁能想到PDA是‘premium discount amount’啊?我乍一看还以为是‘个人数字助理’呢。”大Z回头看小Y一眼说道,“要不还是给它写完整吧,也不是多复杂的单词,好不好?”小Y连连点头答应。

回到自己的座位上,小Y就开始这个改名操作。“这还不小菜一碟么?”小Y心想。打开编辑器,全项目检索“getPDA”,哎唷,还不少,找到十多个,没关系,全部替换成“getPremiumDiscountAmount”。打完收工。

第二天,又到该发版本的时候了。大Z挨个问大家,这个版本做了什么大修改。问到小Y这儿,大Z多问了一句:“昨天你那个函数改名,改完测试过没有?”

“测过,不光我测了,还找测试的同学也帮忙测了。”小Y信心满满地答道。

小Y的信心也就保持了大概不到一小时。他们的系统刚上线,另一支做电商网站的团队就把投诉电话打了过来,说小Y他们的服务报500错误了。大Z在电话里低声下气地道完歉,放下电话也没多说什么,带着团队立即开始查bug。没多久,大家的眼睛都聚焦在这段代码上:

public class OrderService {
    public double getPremiumDiscountAmount(long orderId) {

“这个方法是对外暴露成微服务的,”大Z沉声说道,“我记得本来叫getPDA对吧?你怎么说改就给改了?别人用我们的服务,引用的方法名还没改呢。现在可好,造成生产事故了。”

正确的改名方法

人生没有重来一次的机会,不过我们这个虚构的故事可以有。时间倒退回星期四下午,掌握了正确重构手法的小Y这次会如何给这个函数改名呢?

他会首先新建一个名为getPremiumDiscountAmount的方法,保持原来的getPDA方法不动。这个方法的实现可以直接从原来的getPDA方法拷贝过来。

public class Order {
    public double getPDA() {
        return isPremium ? totalAmount * .15 : 0;
    }

    public double getPremiumDiscountAmount() {
        return isPremium ? totalAmount * .15 : 0;
    }

毫无疑问,这一步修改对系统的行为不会有任何影响,因为新的方法没人使用。下一步,小Y可以让旧方法调用新方法。如果旧方法接受任何参数的话(这个例子里没有),直接原样传递给新方法即可。

public class Order {
    public double getPDA() {
        return getPremiumDiscountAmount();
    }

    public double getPremiumDiscountAmount() {
        return isPremium ? totalAmount * .15 : 0;
    }

这一步修改,一般来说也不会对系统的行为有任何影响,不过还是应该测试一下,确保没有犯任何错误。如果真的犯了错误(虽说真的不太可能),小Y也可以很快撤销修改,回到系统完好的状态。

到这一步,大概用不了两分钟时间。接下来,小Y要开始修改使用getPDA方法的代码了。他可以全局搜索“getPDA”字样,看清楚是不是在调用Order类的getPDA方法;如果是,就改成调用getPremiumDiscountAmount。这个过程中,他就会看到OrderService里的这段代码:

public class OrderService {
    public double getPDA(long orderId) {
        ...
        return order.getPDA();
    }

小Y看见OrderService类也有一个叫getPDA的方法,很明显是当初起名字的时候一脉相承的。要不要把这个名字也改了呢?小Y想一想,还是暂时不要了,先把手上在做的改名动作做完吧。于是他修改的结果是这样:

public class OrderService {
    public double getPDA(long orderId) {
        ...
        return order.getPremiumDiscountAmount();
    }

其他使用getPDA方法的地方也如法炮制,逐个修改过来。每修改一处,小Y都及时测试。

不用太长时间,所有使用Order类的getPDA方法的代码都改成调用getPremiumDiscountAmount方法了。这时小Y并不急于删除已经没人调用的getPDA方法。他先提交代码,看到持续集成通过,再检查这个方法确实没有被其他外部系统调用,然后再删除getPDA方法。

跨越系统边界的改名

完成前面一步改名以后,小Y还是对OrderService里的getPDA这个方法名耿耿于怀。但他在动手改名之前,首先检查了这个方法的作用域,然后发现这个方法是作为服务接口暴露到系统外部的。也就是说,小Y不能自己动手修改所有使用这个方法的代码,他只能提醒别人修改。

改名的前两步操作还是一样,小Y首先新建一个getPremiumDiscountAmount方法,把原来的getPDA方法委派给新方法。然后,小Y需要提醒所有使用getPDA方法的人,叫他们改为使用新的方法。“提醒别人不要使用一个方法”这事,Java语言有一个专门的标记:@deprecated。于是小Y把旧的方法标记为@deprecated,并说明原因。

public class OrderService {
    @Deprecated
    /**
     * @deprecated
     * Replaced by getPremiumDiscountAmount
     * To be removed at May 10th
     */
    public double getPDA(long orderId) {
        return getPremiumDiscountAmount(orderId);
    }

    public double getPremiumDiscountAmount(long orderId) {
        ...
        return order.getPremiumDiscountAmount();
    }

小Y不仅告诉这个服务的使用者,getPDA方法已经被getPremiumDiscountAmount所取代,还明确给出了旧方法删除的时间。由于目前使用这个服务的都是同一家公司的内部团队,小Y只需要在这个版本发布后用邮件告诉其他相关团队,给出的删除日期也是不远的将来。如果这是一个放在公网上给大众使用的开放API,那么信息发布的方式必须更正式,并且留给使用者调整的时间也需要更长。

做完了所有这些沟通工作,到预先提醒的删除日期,小Y才最终删除应该已经没人使用的getPDA方法,总算是完成这次改名操作。

转载自公众号: 程序员练功房

Kiwi

Valar Morghulis

文章评论(0)