《代码大全》程序员入门之道

前言

《代码大全》(Code Complete 2)是一本经典的软件开发指南,内容贯彻整个软件开发的生命周期,它能帮助我们充分理解软件开发各个阶段需要做什么和怎么去做,涵盖代码编写,调试,测试,优化等方面的技巧和最佳实践,帮助程序员编写高质量的代码。其内容通俗易懂,也被行业称为“新手圣经”。如果说你要成为一名优秀的软件工程师,走上一条漫长的道路,而这便是你的入门之道。

关于这书讲了什么?以下是关于我的一些看法,简单为大家分析一下希望大家能感兴趣。

一、让软件开发变得简单

1. 用隐喻来理解软件开发

重要的研发成果常常产自类比(analogy)。

通过把你不太理解的东西和一些你较为理解、且十分类似的东西做比较,你可以对这些不太理解的东西产生更深刻的理解。这种使用隐喻的方法叫做建模(modeling)。

常见的软件隐喻有把软件开发比作写作、比作耕作、比作牡蛎养殖和比作建造。因此软件构建可以隐喻为,建造(building)写作(writing)或是培育(growing)软件。

相对于其他学科而言,软件开发还是一门很年轻的学科,它还没有成熟到拥有一套标准隐喻的程度。因此必然存在许多或相互补充、或相互抵触的隐喻。某些隐喻相对好一些,而另一些则比较糟糕。你对隐喻有多理解,也就决定了你对软件开发有多理解。

2. 前期准备

三思而后行

软件开发的前期准备至关重要,软件开发前期要做哪些准备?

  • 辨别所从事的软件类型。 不同的软件软件项目需要在“准备”和“构建”之间做出不同的平衡,根据开发风格进行分类,采取最适合的实践方法。在序列开发和迭代开发之间做出选择。
  • 定义软件的“问题”。只关注“问题是什么”而不涉及解决方案。解决问题的第一步先把问题称述清楚。
  • 书写正式的软件“需求”。详细描述软件系统应该做什么,确保需求的质量与完备性,将需求清楚地描述。
  • 搭建正确的软件架构。思考架构的每个组成部分(参考架构的典型组成部分,软件架构的设计对入门的程序员有很大的难度,可以先初步了解)。
  • 做好构建的准备工作:
    1. 选择适合项目的编程语言;
    2. 对编程进行约定;
    3. 确定在技术浪潮中的位置;
    4. 选择主要的构建实践方法;

3. 软件构建与设计

“软件设计”一词意味着去构思、创造或发明一套方案,把一份计算机软件的规格说明书要求转变为可实际运行的软件。

设计就是把需求分析和编码调试连在一起的活动。好的高层次设计能提供一个可以稳妥容纳多个较低层次设计的结构。好的设计对于小型项目非常有用,对于大型项目就更是不可或缺。

软件的首要技术使命是管理复杂度

理想的设计要将以下特征考虑在范畴内:最小的复杂度;易于维护;松散耦合(程序各个组成部分直接关联最小);可扩展性;可重复性;高扇入(让大量的类使用某个给定的类如工具类);低扇出(让一个类里少量或适中地使用其他的类);可移植性;精简性;层次性;标准技术(用标准化和常用的方法)

通过分层次的设计可以简化设计,设计的层次分为五层:

  • 软件系统层
  • 分解为子系统或包
  • 分解为类
  • 分解为子程序
  • 子程序的内部设计

二、写出高质量代码

1. 让类可以工作

如何写出一个好的类,首先要理解面向对象编程,理解面向对象的四大特性:封装、抽象、继承、多态。

抽象数据类型ADT

要想理解面向对象编程,首先要理解 ADT。

不懂 ADT 的程序员开发出来的类只是名义上的“类”而已——实际上这种“类”只不过就是把一些稍有点儿关系的数据和子程序堆在一起。然而在理解 ADT之后,程序员就能写出在一开始很容易实现、日后也易于修改的类来。

概念:抽象数据类型(Abstract Data Type, ADT)是一种数据类型,它定义了一组可能的值以及可以对这些值执行的操作。ADT的目的是提供一种方式来描述数据结构的行为,而不涉及其具体的实现细节。这样,ADT允许程序员在设计软件时关注于“什么”而不是“如何”。

定义

  • 数据:ADT包含的数据类型和数据结构,以及它们之间的关系。
  • 操作:可以对这些数据执行的操作,通常包括创建、插入、删除、查找、修改等。
  • 接口:ADT提供了一组公共接口,这些接口定义了与ADT交互的方式。
  • 约束:ADT可能有一组预定义的约束或规则,这些约束限制了数据的使用和操作。

ADT的关键特点是其抽象性

  • 抽象性:ADT隐藏了数据的具体表示和操作的实现细节。用户只需知道操作的功能和如何使用它们,而不必了解它们是如何实现的。
  • 封装:ADT封装了数据和对数据的操作,用户只能通过定义良好的接口与ADT交互。
  • 独立性:ADT的实现与其使用是独立的。这意味着ADT的实现可以改变,而不会影响到使用它的程序

抽象数据类型构成了类这一概念的基础。


实现好的抽象

概念:抽象是一种以简化的形式来看待复杂操作的能力。类的接口为隐藏在其后的具体实现提供了一种抽象。类的接口应能提供一组明显相关的子程序。

  • 类的接口应该展现一致的抽象层次:每个类应该实现一个ADT
  • 理解类所实现的抽象是什么:这里的“抽象”指的是类所表示的概念或实体,以及它应该提供的行为和功能。
  • 提供成对的服务:添加相应、相等以及相反的操作
  • 谨防在修改时破坏接口的抽象
  • 保持添加的公共成员与接口抽象一致
  • 尽可能让接口可编程而不是表达语义
  • 考虑抽象性和内聚性

实现好的封装

概念:封装是一个比抽象更强的概念。抽象通过提供一个可以让你忽略实现细节的模型来管理复杂度,而封装则强制阻止你看到细节——即便你想这么做。

  • 尽可能地限制类和成员的访问性
  • 避免把私用的实现细节放入类的接口中
  • 不要对类的使用者做出任何假设
  • 避免使用友元类
  • 不要因为一个子程序里仅使用公用子程序,就把它归入公开接口
  • 让阅读代码比写代码更方便
  • 要格外警惕从语义上破坏封装性

继承(是一个...关系)

概念:继承是一个类是另一个类的一种特化(specialization)。继承的目的在于,通过“定义能为两个或更多个派生类提供共有元素的基类”的方式写出更精简的代码。其中的共有元素可以是子程序接口、内部实现、数据成员或数据类型等。继承能把这些共有的元素集中在一个基类中,从而有助于避免在多处出现重复的代码和数据。


多态

想要用好继承,必须了解多态,继承和多态是面向对象编程(OOP)中的两个核心概念,它们通常是一起使用的。为了有效地使用继承,理解多态是非常重要的。

概念:多态(Polymorphism)是指能够在多个不同的类中定义相同的操作或属性名,但在每个类中可以有不同的实现。多态的关键在于,可以在运行时根据对象的实际类型来决定调用哪个方法。这意味着,多态允许我们以统一的方式处理不同类型的对象,而不必关心它们的具体类。

在继承的上下文中,多态通常通过以下两种方式实现:

  • 方法覆盖(Override):子类可以提供一个与父类中同名的方法,但具有不同的实现。当子类的对象调用这个方法时,会执行子类中的版本,而不是父类中的版本。这使得子类能够提供特定的行为,同时保持接口的一致性。
  • 方法重载(Overload):在同一个类中,可以定义多个同名的方法,但参数列表不同。这使得可以在不同的上下文中使用相同的方法名,增加了代码的灵活性和可读性。

为了有效地使用继承,必须理解多态的原理,因为多态是继承的强大之处。通过多态,我们可以编写更通用、更灵活的代码,因为我们可以设计出能够处理父类型对象的函数或方法,而这些父类型对象可以是任何子类的实例。这样,我们的代码就不需要知道具体的子类,只需要与父类定义的接口交互即可。

例如,假设我们有一个基类Animal,它有一个方法makeSound()。我们可以创建多个子类,如DogCat,每个子类都覆盖makeSound()方法以发出不同的声音。使用多态,我们可以创建一个函数animalSound(Animal animal),它接受一个Animal类型的参数,但可以处理任何Animal的子类对象。当我们调用animalSound()函数时,它会根据传入的实际对象类型调用相应子类的makeSound()方法。

继承和多态是面向对象编程中紧密相关的概念。理解多态不仅能够帮助我们更好地使用继承,还能够让我们编写出更加模块化、可扩展和易于维护的代码。


什么时候需要创建类?

  • 对现实世界中的对象建模对抽象对象建模
  • 降低复杂度
  • 隔离复杂度
  • 隐藏实现细节
  • 限制变化所影响的范围
  • 隐藏全局数据
  • 让参数传递更顺畅
  • 创建中心控制点
  • 让代码更易于重用
  • 为程序族做计划
  • 把相关操作放到一起
  • 实现特定的重构

2. 高质量的子程序(函数)

子程序的一些约定:

  • 好的子程序名字应该描述子程序所做的所有事情,描述返回值,名字不要太长,动词加宾语的形式,避免使用含糊不清的动词,使用对仗词的命名规则。
  • 子程序的长度,最好不要超过200行,超过了要注意子程序的可读性。
  • 子程序的参数排列顺序:先输入-修改-输出的参数排列方式。不同的子程序使用的参数一致的话,那他们的参数排列方式也应该一致。
  • 对参数的假定进行说明,参数个数大约在7个以内,不影响可读性
  • 检查所有可能的返回值

组织直线型代码

  • 必须有明确的顺序的语句
  • 使代码易于自上而下阅读
  • 把相关的语句组织在一起

什么时候创建子程序?

  • 降低复杂度
  • 引入中间的、易懂的抽象
  • 避免代码重复
  • 支持子类化
  • 隐藏顺序
  • 隐藏指针操作
  • 提高可移植性
  • 简化复杂的逻辑判断
  • 改善性能

除此之外,创建类的很多理由也是创建子程序的理由:

  • 隔离复杂度
  • 隐藏实现细节
  • 限制变化所带来的影响
  • 隐藏全局数据
  • 形成中央控制点
  • 促成可重用的代码
  • 达到特定的重构目的

3. 使用变量的一般事项

创建有效数据的第一步是了解所要创建数据的种类。积累大量的数据类型对于程序员来说是至关重要的

认识数据类型

数据类型的表示范围

byteshortintlongfloatdoublecharboolean
存储空间(字符)1248482-
表示范围-128~127-2^15 ~ 2^15-1-2^31 ~ 2^31-1-2^63 ~ 2^63-1-3.403E38 ~ 3.403E38-1.798E308 ~ 1.798E308true/false
无符号0~2550~2^16-10~2^32-10~2^64-1----

变量的定义

  • 关闭隐式声明
  • 声明全部的变量,确定数据类型,作用域
  • 遵循某种命名规则,一个变量只有一个变量名

变量的初始化原则

  • 在声明变量的时候初始化
  • 在靠近变量第一次使用的位置初始化
  • 理想情况第一次使用变量的位置声明和定义该变量。
  • 不再改变的变量设置用final(Java)或const(C++)修饰。

变量的作用域

“作用域”可以看作是一种衡量变量的知名度的方法:它的名气有多大?作用域或者可见性(visibility) 指的是变量在程序内的可见和可引用的范围。

  • 使变量引用局部化,变量定义靠近使用时的位置,提高代码可读性
  • 尽可能的缩短变量的“存活”时间:减少第一次和最后一次使用变量的代码行数
  • 减小变量的作用域:在循环外初始化使用的变量;直到变量即将被使用时赋值;把相关语句放到一起或提取成独立的子程序;声明作用域使变量局部化。

变量的命名

为变量命名时最重要的考虑事项是,该名字要完全、准确地描述出该变量所代表的事物。获得好名字的一种实用技巧就是用文字表达变量所代表的是什么。

  • 以问题为导向,好的命名表达的是“什么”而不是“如何”

  • 适当的长度,了解一些可以缩写的名词和可以去掉的介词。如numberOfTeamMembers写成numTeamMembers。

  • 计算值的限定词放在最后,比如Total、Sum、Max、Min、Average。放在最后sizeMax。

  • 变量名的对仗使用。old对new,first对last,方便理解

  • 特定类型的数据命名:

  1. 常量全部大写单词用下划线隔开;
  2. 枚举用组前缀Color_来描述;
  3. 记住典型的布尔变量名done,error,found,success或ok...;给变量赋予真/假的含义;使用肯定的布尔变量命名
  4. 临时变量:使用更有描述性的变量名而不是单纯的temp;
  5. 状态变量:标记应该用枚举类型、具名常量。不用flag进行标记;
  6. 循环索引:循环越复杂使用更有描述性索引,而不是简单的i,j,k;
  • 避免令人误解的命名

  • 避免具有相似含义的命名:用Count或Index代替Num

  • 避免包含数字的命名

  • 避免只有一两个字符不同的变量名

4.条件和控制循环

使用if语句

  • 先写正常代码路径;处理最常见的情况,再处理不常见情况。
  • 确保对于等量的分支是正确的
  • 利用布尔函数调用简化复杂的检测
  • 确保所有的情况都考虑到了

使用case语句

  • 为case选择最有效的排列顺序:按字母或数字排列;正常情况执行频率高的在前面;
  • 简化每种情况对应的操作。
  • default只用于检查真正的默认情况,不要失去case的标号所提供的自动说明功能。

循环的种类

  • 计数循环(执行次数一定)for循环
  • 连续求值的循环(到了某个特定条件结束循环)while(condition == true){}
  • 无限循环(特定条件永远无法到达)while(true){}
  • 迭代器循环(对容器类的每个元素执行一次操作)for each

循环使用的注意事项

  • 非特定条件避免无限循环,设置可靠的计数器,确保循环能够停下来
  • 获取结果后立即停止循环break
  • 在while循环开始进行判断处使用continue,跳过不常见情况的循环体
  • 循环下标变量的作用域限制在循环体内
  • 循环体要尽可能地短,以便可以一目了然
  • 把嵌套限制在3层以内
  • 把长循环的内容包装成函数
  • 由内而外创建循环

5.表驱动方法

  • 表驱动法是一种编程模式,从表里面查找信息而不使用逻辑语句(if和case)。
  • 当逻辑链很长可以通过索引表或键值对表选择事物。
  • 表提供了一种复杂逻辑和继承结构的替代方案。

三、优化代码效率

1. 关于软件质量

软件质量的特性

  • 软件同时拥有外在的和内在的质量特性。外在特性指的是该产品的用户所能够感受到的部分,包括下列内容。正确性、可用性、效率、可靠性、完整性、适应性、精确性、健壮性、可维护性、灵活性、可移植性、可重复性、可读性、可测试性、可理解性。

改善软件质量

  • 关注开发过程
  • 确立软件质量的目标:根据不同性质的侧重点不同,明确定义出软件质量的目标,程序员才能极力增强你所强调的特性。
  • 将软件质量保障工作贯彻到每个阶段

2.如何进行调试

关于调试

  • 理解你正在编写的程序;
  • 明确你犯了那种类型的错误;
  • 从代码阅读者的角度分析代码质量;
  • 审视自己解决问题的方法,花点时间来分析并改善你的调试方法,可能就是减少程序开发时间的最好方法;
  • 审视自己修改正缺陷的方法。

寻找缺陷步骤

  • 把错误状态稳定下来,也就是能让缺陷稳定的重现,这几乎是最有挑战的工作之一;
  • 确定错误的来源;
  • 修补缺陷;
  • 对修补的缺陷进行测试;
  • 查找是否还有类似的错误。

修复缺陷

  • 在动手之前先要理解问题,知道你能真正理解问题,每次都能正确地预测结果为止;
  • 理解程序本身,而不仅仅是问题;
  • 验证对错误的分析;
  • 放松一下;
  • 保存最初的源代码,至少你能对新旧代码进行比较,看到底改了哪些地方;
  • 治本,而不是治标;
  • 修改代码时一定要有恰当的理由;
  • 一次只做一个改动;
  • 检查自己的改动;
  • 增加暴露问题的单元测试;
  • 搜索类似的缺陷,如果你想不出如何查找类似缺陷,这就意味着你还没有完全理解问题。

调试工具

  • 源代码比较工具;
  • 编译器的警告信息,把编译器的警告级别设置为最严格;
  • 增强的语法检查和逻辑检查;
  • 执行性能剖测器;
  • 测试框架;
  • 调试器;

3.如何进行重构

软件演化类型

  • 在修改中软件的质量要么提高,要么恶化。
  • 软件演化的基本原则就是,演化应当提高程序的内在质量。

重构的理由

  • 代码重复:DRY,Do not Repeat Yourself;
  • 冗长的子程序:很少会用到长度超过一个屏幕的子程序。改善方法是提高其模块性-增加定义完善、命名准确的子程序,让他们各自集中力量做好一件事;
  • 循环过长或嵌套过深;
  • 内聚性太差的类;
  • 拥有太多参数的参数列表;
  • 变化导致多个类的相同修改;
  • 同时使用的相关数据并未以类的形式组织;
  • 过多使用基本数据类型;
  • 某个类无所事事;
  • 中间人对象无事可做;
  • 子程序明明不恰当。只要看到某个子程序命名有问题,就应该立即着手修改。
  • 注释被用于解释难懂的代码。不要为拙劣的代码编写文档——应当重写代码;
  • 程序中的一些代码似乎是在将来的某个时候才会用到。对未来需求有所准备的代码并不是编写大量空中楼阁式的代码,而是尽可能将满足当前需求的代码清晰明白的表现出来,使未来的程序员理解这些代码到底完成了什么功能,没完成什么功能,以便根据他们的需求进行调整。

数据级的重构

  • 用具名常量代替神秘数值;
  • 使变量的名字更加清晰且传递更多信息;
  • 用函数代替表达式;
  • 将基础类型转化为类。如果一个基础类型需要额外的功能或者额外的数据,那么就应该把基础类型转化为类,并添加你所需要的行为;
  • 将一组类型码转换为类或者枚举类型;

语句级的重构

  • 分解布尔表达式
  • 将复杂布尔表达式转换为命名准确的布尔函数;
  • 在嵌套的if-then-else语句中应该一知道答案就返回,而不是赋值给一个返回值

子程序级重构

  • 提取子程序或者方法
  • 将冗长的子程序转换为类
  • 合并相似的子程序,通过参数区分他们的功能

类接口的重构

  • 将成员函数放到另一个类中
  • 将一个类变为两个类。如果一个类中的成员函数完成两种截然不同的功能,将这样的类转换为两个类
  • 对于不能修改的类成员去掉其set函数。如果一个成员在对象被创建时设置,之后遍不再允许修改,那么删除其set函数,而是在构造函数中进行初始化。

系统级重构

  • 使用工厂函数而不是简单的构造函数;
  • 用异常代替错误代码,或者反其道而行之,取决于你的错误处理策略,请确保代码使用了标准的错误处理方法。

安全的重构

  • 保存初始代码。要保证你还能回到代码的初始状态。
  • 重构的步伐请小些。这样才能理解所做修改对程序的全部影响。
  • 同一时间只做一项重构。在进入到下一项重构之前,对代码进行重新编译和测试。
  • 把要做的事情一步步列出来。写出一份重构列表能够让你在修改时保持思路连贯。
  • 设置一个停车场。把你需要在未来进行而现在可以暂且放在一遍的修改工作记录下来。
  • 利用编译器警告信息。把编译器的警告级别设置为最严格。
  • 重新测试。应该把重新测试作为修改代码工作的补充。
  • 检查对代码的修改。如果说第一次运行程序时检查代码是必要的,那么接下来修改代码的过程中,时
  • 检查代码则更为必要。另外,应该把简单的修改当作复杂的修改对待。
  • 根据风险级别来调整重构方法。尤其是对于有一定风险的重构,谨慎才能避免出错。

重构策略

  • 在增加子程序的时候重构
  • 在添加类的时候重构
  • 在修补缺陷的时候重构
  • 关注于易出错的模块
  • 关注高度复杂的模块
  • 在维护环境下,改善你手中正在处理的代码。如果你正在维护某部分代码,请确保代码在离开你的时候比来之前更健康
  • 开发阶段的重构是提高程序质量的最佳时机。

四、养成习惯

1.实现良好布局

关于与计算机交流和与人交流的差异,布局问题大概比编程中的其他任何方面都更明显。编程工作量的一小部分是写让机器读的程序,大部分工作是写能让他人看懂的程序。

良好布局的目标

  • 准确表现代码的逻辑结构
  • 始终如一保持逻辑结构
  • 改善可读性
  • 经得起修改

这里可以参考阿里巴巴代码规范

2.注释习惯

  • 该不该注释是个需要认真对待的问题。差劲的注释只会浪费时间,帮倒忙,好的注释才有价值。
  • 好代码本身就是最好的说明。如果代码太糟,需要大量注释,应先试着改进代码,直至无须过多注释为止。
  • 注释应说出代码无法说出的东西---例如概述或用意等信息。
  • 有的注释风格需要许多重复性劳动,应舍弃之,改用易于维护的注释风格。

3.个人性格

聪明与谦虚

Edsger Dijkstra 在 1972 年的图灵奖演讲会上宣读了一篇名为《The Humble Programmer》(《谦的程序员》的文章。他认为大部分编程工作都旨在弥补我们有限的智力。精通编程的人是那些了解自己头脑有多人局限性的人,都很谦虚。而那些编程糟糕的人,总是拒绝接受自己脑瓜 不能胜任工作的事实,自负使得他们无法成为优秀的程序员。承认自己智力有限并通过学习来弥补,你会成为更好的程序员。你越是谦虚,进步就越快。

求知欲

  • 在开发过程中建立自我意识 你越了解软件开发过程,无论通过阅读还是通过自己对软件开发的观察,你就越能理解变化,使团队朝着正确的方向发展。如果分配给你的工作净是些不能提高自身技能的短期任务,你理应表示不满。如果正处于竞争激烈的软件市场,则目前工作用到的一半知识将在三年后过时。假如不持续学习,你就会落伍。
  • 试验 对编程和开发过程做试验,是学习编程的有效途径之一。如果不了解所用语言的某一特性是怎么回事,可编写一个小程序来检验,看看它是如何工作的。请在调试器中观察程序的执行情况。用个小程序来检验某一概念,总比编写大程序时运用不太了解的特性要好。
  • 阅读解决问题有关的方法
  • 在行动之前做分析和计划
  • 学习成功项目的开发经验
  • 阅读文档和其他书本期刊
  • 同专业人士交往

诚实

编程生涯成熟的部分标志就是发展出一种不屈不挠的诚实感。

  • 不是高手时不假装是高手。
  • 乐于承认错误。
  • 力图理解编译器的警告,而非弃之不理。
  • 透彻理解自己的程序,而不要只是编译看看能否运行
  • 提供实际的状况报告。
  • 提供现实的进度方案,在上司面前坚持自己的意见。

交流与合作

真正优秀的程序员知道怎样同别人融洽地工作和娱乐。代码便于看懂是对团队成员的要求之一。计算机可能和别人一样频繁地读你的代码,但是它读质量差的程序可比人强多了。作为一项可读性原则,应该把修改你代码的人记在心上。编程首先是与人交流,其次才是与计算机交流。

创造力和纪律

精致的程序作品也要求许多约束。如果编程之前不分析需求也不设计,编写代码的过程中你会发现要了解许多东西。不要将创造力花到无关紧要的事物上,在非关键之处建立规范,在重要的地方倾力你的创造力。

懒惰

偷懒要放在节省时间上,而不是浪费时间上。偷懒有三种表现:一是拖延不喜欢的任务,容易导致效率低下甚至导致完成不了任务。二是迅速完成不喜欢的任务,以摆脱之。这种偷懒比较开明,能够解决问题。还有一种一劳永逸的偷懒,便是编写某个工具来完成不喜欢的任务,以便再也不用做这样的事情了。这种便是智慧的偷懒。

出乎意料的性格

  • 坚持:多数时候软件开发中的坚持其实就是没有好处的“固执”。当在某段新代码上 卡壳时,坚持很难让人称道。不妨另辟蹊径,尝试重新设计类,或者绕过去,以 后回头再试。当一种办法行不通时,正好可以换个办法试试(Pirsig 1974)。
  • 经验:人们还荒唐地强调程序员有多少经验。“我们需要有五年以上 C 语言编程经验的程序员”就是愚蠢的说法。如果程序员过了前一两年还没有学好 C 语言,那么再加三年也没什么意义。这种“经验”和工作效能关系不大。程序开发中的信息快速更新,导致“经验”也跟着高速变化。在很多行业中有成就的专业人士可以度假、休息,尽享成功带来的荣誉;而在软件开发行业,任何人放松下来就很快跟不上形势。为了让自己仍然有用,你必须紧跟潮流。对于求知欲强的年轻程序员来说,这会是他们的优势:而老程序员常常自认为有资历,讨厌年复一年地证明自己的能力。
  • 编程狂人:彻夜编程让你感觉像是世上最好的程序员,却要花几个星期去纠正你在短暂辉煌时埋下的失误。可以热爱编程,但热情不能代替熟练的能力,请想明白什么更重要。

习惯

初涉某事时,就应端正态度来学。开始做事情时,你还会积极思考,轻松决定做得好坏。干了一段时间后,就会习以为常,“习惯的力量”开始起作用。请确保这些习惯是你所希望的东西。

如果没养成最有效的习惯,该怎么办呢?如何改掉坏习惯?

不能用“没有习惯”来代替“坏习惯”--这就是人们骤然停止抽烟、停止咒骂或者停止多食时会很难受的原因,除非有了替代方法,如嚼口香糖。以新习惯来代替老习惯,要比干脆戒除老习惯容易。对于编程,要试着养成有用的新习惯。举个例子,要培养先以伪代码编写类再改用实际代码,以及编译前认真检查代码的习惯。不必为失去坏习惯而多虑,有了新习惯,坏习惯自然就会消失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值