代码重构之[神秘命名]

引言

在接触他人设计的接口时,我年少时总是怀着一颗好奇的心,深入探究其实现细节。然而随着工作经验的积累,我逐渐意识到这种方式效率低下。如今,只需要浏览接口名称,我便能迅速理解其功能。

但是,大多数时候接口的名称并不能直观的揭示其实际作用。即便我已经摒弃了"深入研究"的低效阅读方式,但由于接口名称不够直观清晰,我仍需要深入代码的核心,一探究竟。

在阅读《重构》后,我结合自己的工作经验,深入思考了书中的内容,并撰写了这篇博客。这篇博客将为你解答以下三个问题:

  1. 什么叫作「神秘命名」?如何识别项目中的「神秘命名」?
  2. 神秘命名」存在什么问题?为什么一定要重构它?
  3. 如何修改「神秘命名」?有什么通用的方法吗?

特征

言行不一

所谓"言行不一",是指名称描述的是A,但内容是B。下面是两个简单的示例:

csharp 复制代码
//该命名描述的是DoA,但函数内容是DoB
public void DoSomethingA()
{
    //dosomething B
}
csharp 复制代码
//该命名描述的是"描述文本",但内容是浮点数(浮点数应该是价格)
public string Describe = "0.1f";

暗中作祟

所谓"暗中作祟",是指名称只描述了A,但内容不仅包含了A,还包含B。下面是两个简单的示例:

csharp 复制代码
public void DoSomthingA()
{
    //dosomething A
    //dosomething B
}

不知所谓

所谓"不知所谓",是指名称过于模糊,读者无法通过名称感知到其作用,必须通过阅读上下文去猜测。

ini 复制代码
public int a = 100;
public float b = 0.1f;

总结以上三种现象,我们发现:不能够直接、准确的解释其内容的命名,叫作「神秘命名」


为什么重构?

  1. 可读性差,理解成本高。调用者无法仅通过浏览名称就了解整段代码的作用,必须花费更多的时间阅读其中的内容。
  2. 可维护性差,修改成本高。修改者在修改一个函数时,无意间就会扩大影响范围。如在"暗中作祟"中,修改者仅仅需要修改 dosomething A ,但还需要时刻注意 dosomething B ,否则就会误改到其他功能。

如何重构?

修改名称

傻瓜都能写出计算机可以理解的代码,唯有能写出人类容易理解的代码,才是优秀的程序员。

为了提高代码的可读性,我们应该尽量使用描述性强、准确的名称来描述函数、变量和类 等一切需要命名的代码。名称应该强调的是做什么,而非怎么做

csharp 复制代码
public void DoSomethingB()
{
    //dosomething B
}
ini 复制代码
public string Describe = "this is a string for describe";
ini 复制代码
public int score = 100;
public float price = 0.1f;

提炼函数

如果你还在思考怎么给出一个好名字,去准确描述这段代码在做什么事,说明背后很有可能藏有更深的设计问题。为一个恼人的名称所付出的纠结,常常能够推动我们对代码进行精简。

csharp 复制代码
public void DoSomthingA()
{
    //dosomething A
}

public void DoSomethingB()
{
    //dosomething B
}

真实案例

下面是某个MVC结构的项目中,一个Model层的函数

csharp 复制代码
public bool IsNeedUploadQuestionData(bool clearData = false)
{
    if (!string.IsNullOrEmpty(curOutputKey))
    {
        if (clearData)
        {
            JsonData data = new JsonData();
            data.SetJsonType(JsonType.Array);
            refDatasMap[curOutputKey] = data;
        }
        return true;
    }
    return false;
}

这个函数做了两件事:

  1. 根据一些规则,返回给外部一个布尔值,该布尔值代表「是否需要上传问题数据」。
  2. 根据传入的参数 clearData ,在函数内部进行其他数据操作。

然而,反映出来的问题也有两个:

  1. IsNeedUploadQuestionData 是业务上的名称,如果不熟悉这块业务的调用者,就无法仅通过名称就了解到该函数的实际作用。
  2. IsNeedUploadQuestionData 这个名称只描述了第一个功能点,第二点却无从体现。

我建议这样重构:

第一步 复制代码
public bool IsNeedUploadQuestionData()
{
    if (!string.IsNullOrEmpty(curOutputKey))
    {
        return true;
    }
    return false;
}

//先将数据填充功能提炼出来
public void InitCurOutputKeyData(bool isClearData = false)
{
    if(string.IsNullOrEmpty(curOutputKey) || !isClearData)
    {
        return;
    }
    JsonData data = new JsonData();
    data.SetJsonType(JsonType.Array);
    refDatasMap[curOutputKey] = data;
}   
第二步 复制代码
public bool IsCurOutputKeyValidate()//函数改名
{
    if (!string.IsNullOrEmpty(curOutputKey))
    {
        return true;
    }
    return false;
}

public void InitCurOutputKeyData(bool isClearData = false)
{
    if(string.IsNullOrEmpty(curOutputKey) || !isClearData)
    {
        return;
    }
    JsonData data = new JsonData();
    data.SetJsonType(JsonType.Array);
    refDatasMap[curOutputKey] = data;
}   
最后 复制代码
//curOutputKey 字段是否合法
public bool IsCurOutputKeyValidate()
{
    return !string.IsNullOrEmpty(curOutputKey);
}

//初始化 curOutputKey 的数据信息,并存放在字典里
public void InitCurOutputKeyData(bool isClearData = false)
{
    if(!IsCurOutputKeyValidate() || !isClearData)
    {
        return;
    }
    JsonData data = new JsonData();
    data.SetJsonType(JsonType.Array);
    refDatasMap[curOutputKey] = data;
}
  1. 为了 Model 层的代码能够复用,我拒绝直接以业务命名,而是修改名称为 IsCurOutputKeyValidate ,这是更加直观的名称。调用者只需浏览函数名称,就知道这个函数的作用是:判断 curOutputKey 字段是否合法。这样函数名称和其实际功能就达到了"言行一致"。
  2. 我建议将第二个功能点单独提取出来,作为一个新的函数 InitCurOutputKeyData 。该函数的作用是:以 curOutputKey 字段为键,初始化对应值。这样每个函数的功能就相互独立了,我们也不必再为命名纠结了。
相关推荐
这个DBA有点耶1 天前
MySQL 8.0执行计划分析利器:EXPLAIN ANALYZE到底比EXPLAIN强在哪?
数据库·mysql·代码规范
独立开发者屿风2 天前
在 React 里把 import 排得明明白白:一份让人极度舒适的配置指南
react.js·代码规范
行者全栈架构师2 天前
WorkBuddy 开放平台个人开发者接入实战:从零到 Agent 应用的完整路径
人工智能·架构·代码规范
打呵欠的猫7 天前
一个 Hook 让 AI 每次写文件前自动检查编码规范,违规代码再也提交不进来
前端·ai编程·代码规范
Asize8 天前
框架的说明书是写给 AI 看的:我用 Next.js 搭了个博客
人工智能·代码规范·next.js
梦梦代码精12 天前
深度体验BuildingAI:一款集成商业闭环的开源智能体平台
人工智能·docker·开源·代码规范
浮生望13 天前
ESLint Flat Config 工程化实战:从规则配置到团队代码规范落地
代码规范
电子科技圈13 天前
IAR与东软睿驰达成战略合作,强化软件开发效率与生态协作
设计模式·开源·软件工程·软件构建·代码规范·设计规范·代码复审
这个DBA有点耶15 天前
大事务的“事前预防”:监控、拦截、Kill,四层防线一次讲透
数据库·mysql·代码规范