🐳 📖

《代码大全》阅读笔记之三:变量

📆 2021-05-03

来源:《代码大全》

🏷 编程阅读

🖍 变量———构建活动中的基本组成成分

🏂 正文 👇

这部分内容主要讲构建活动中的基本组成成分:变量。

变量包括变量名和其所关联的数据。创建有效数据的第一步需要我们了解所要创建数据的种类。不合理地初始化数据是产生编程错误的常见根源之一。一些关于避免产生初始化错误的建议:

  • 在声明变量的时候初始化;
  • 在靠近变量第一次使用的位置初始化;
  • 在类的构造函数里初始化该类的数据成员;
  • 检查是否需要重新初始化;

作用域是变量在程序内的可见和可引用的范围。应该把变量引用局部化,并尽可能的集中起来。使用变量的“跨度(span)”来衡量变量引用的集中程度,跨度小的代码具有更好的可读性。与变量跨度相关的一个概念是“存活时间(live time)”,也就是变量存在期间所跨越的语句总数。“长存活时间”意味着一个变量经历了许多语句,而“短存活时间”意味着它只经历了很少的语句。保持变量短的存活时间的主要好处有:

  • 减小攻击窗口;
  • 能对自己的代码有更准确的认识;
  • 减少初始化错误的可能;
  • 使代码更具可读性;
  • 对代码拆分也很有帮助;

如果用跨度和生存时间的概念来考察全局变量,就会发现全局变量的跨度和生存时间都很长,这也是要避免使用全局变量的好理由之一。应该尽量缩小变量的作用域,程序员采用缩小变量作用域的方法,取决于其如何看待“方便性”和“智力上的可管理性”,也就是侧重于写程序,还是读程序。使作用域最大化可能真的会让程序写起来比较容易(不用考虑信息的隐藏和封装所需要的额外抽象),一个允许任何其子程序在任何时间使用任何变量的程序是更难于理解的。这种程序无论是阅读,调试还是修改起来都很困难。变量的“持续性”有时是很不确定的,可以通过:加入调试代码或者断言来检查关键变量的合理取值;养成在使用所有数据之前声明和初始化的习惯等措施来抵御这种不确定性。变量与值的绑定时间(编写时,编译时,加载时,运行时或者其他时间),会影响代码的灵活性和复杂度。越早绑定灵活性越低,复杂度越高。成功的软件开发需要依赖于将代码的复杂程度降低到最小,因此一个熟练的程序员会按照需要引入足够的灵活性来满足软件需求,但是却不会增加需求范围之外的任何灵活性以及相应的复杂度。同时还要注意在使用变量的过程中,为变量指定单一用途,避免让代码具有隐含含义。

好的变量名是高效编程的重要方面。一个好的变量名是可读的,易记的和恰如其分的。变量的名字要完全,准确地描述出该变量所代表的事物。一个好记的名字反映的通常都是问题,而不是解决方案,表达的是“什么(what)”,而不是“如何(how)”。如果你发现自己需要猜测某段代码的含义的时候,就该考虑为变量重新命名。你没有必要去猜测代码。你应该能直接读懂它们。对于为布尔变量命名,有些程序员喜欢在他们写的布尔变量名前面加上 Is。例如:isDone? isError? isFound? 这种方法的优点之一是它不能用于那些模糊不清的名字:isStatus?毫无意义。它的缺点之一是降低了简单逻辑表达式的可读性:if(isFound) 的可读性要略差于 if(found)。可以在需要的时候,创建自己的变量命名标准。同时要避免不恰当的变量名:

  • 避免使用令人误解的名字或缩写;
  • 避免使用具有相似含义的名字;
  • 避免使用具有不同含义但却有相似名字的变量;
  • 避免在名字中使用数字;
  • 避免在名字中拼错单词;
  • 避免使用多种自然语言;

基本数据类型是构建其他所有数据类型的构造块。对于数值型数据来说:要避免使用“神秘数值(magic number)”;使类型转换变得明显;避免混合类型的比较。对于整数,要检查整数溢出,整数除法。对于浮点数,要注意浮点数的精度相关问题。字符和字符串也要避免使用神秘字符和神秘字符串,在程序生命周期中尽早决定国际化/本地化策略。用布尔变量来简化复杂的判断,增加可读性。枚举类型是一种允许用英语来描述某一类对象中每个成员的数组类型。每当你看到字面形式数字的时候,就应该问问自己,把它换成枚举类型是不是更合理。具名常量很像是变量,但是一旦赋值以后就不能再修改了。使用具名常量是一种将程序“参数化”的方法———把程序中可能变化的一个方面写为参数,当需要对其修改时,只改动一处就可以了,而不必在程序中到处改动。程序员自定义的数据类型是语言所能赋予你的一种最强有力的,最有助于澄清你对程序的理解的功能之一。创建自定义数据类型的指导原则有:

  • 给所创建的类型取功能导向的名字,避免使用那些代表了类型底层计算机数据类的类型名;
  • 避免使用预定义类型,尽可能多地使用自己创建的类型;
  • 不要重定义一个预定义类型;
  • 定义代替类型以便于移植;
  • 考虑创建一个类而不是使用typedef;

此外还有一些不常见的数据类型(相对于面向对象编程来说)。结构体是指使用其他类型组建的数据。用结构体可以明确数据关系,结构体把相关联的一组数据项聚集在一起。如果数据的组织结构非常清晰,那么弄清楚哪些数据与哪些相互关联就会容易很多。结构体也可以简化对数据的操作。因为你可以一次性操作一整块数据,而不是一条一条操作。此外结构体还可以用来简化参数列表,减少维护工作。指针的使用是现代编程中最容易出错的领域之一。从概念上看,每一个指针都包含两个部分:内存中的某处位置,以及如何解释该位置中的内容。同样的原始内存空间可以解释为一个字符串,一个整数,一个浮点数,或者任何其他事物———取决于指向该内存的指针的基类型。通常,指针错误都产生于指针指向了它不应该指向的位置。全局数据是可以在程序中任意一个位置访问的数据。使用全局数据的风险比使用局部数据大。与全局数据有关的问题有:

  • 可能会无意间修改了全局数据;
  • 阻碍代码重用;
  • 与全局数据有关的非确定的初始化顺序事宜;
  • 破坏了模块化和智力上的可管理性;创建超过几百行代码的程序的核心便是管理复杂度。你能够在智力上管理一个大型程序的唯一办法就是把它拆分成几部分,从而可以在同一时间只考虑一部分。模块化就是你手中可以使用的把程序拆分成几部分的最强大工具。全局数据使得你的模块化能力大打折扣。

同时也有一些使用全局数据的合理的理由:保存全局数值;模拟具名常量;模拟枚举类型;简化对极其常用的数据的使用;消除流浪数据等。确保只有在万不得已时才使用全局数据。在选择使用全局数据之前,可以考虑下面的代替方案:

  • 首先把每一个变量设置为局部的,仅当需要时才把变量设置为全局的;
  • 区分全局变量和类变量。如果类外部的子程序需要使用类中的变量,那么就用访问器子程序来提供对该变量的访问。不要直接访问类变量;
  • 使用访问器子程序;

你用全局数据能做的任何事情,都可以用访问器子程序做得更好。使用访问器子程序是实现抽象数据类型和信息隐藏的一种核心方法,可以实现对数据的集中控制,可以很容易地转变为抽象数据类型。

👐 The End 🎉

上一篇 《代码大全》阅读笔记之四:语句🏠下一篇《代码大全》阅读笔记之二:创建高质量的代码 

👇 💬