软件工程师,要么不写代码,要么就写优雅的代码

news2025/1/22 18:05:04

何为优雅的代码

        优雅的代码,至少需要遵循以下几个原则:

        遵守规范

        优雅的代码,首先让人看起来就是很整洁的。而这种整洁,则来源于代码规范。严格地遵守代码规范,是提高且保证代码质量的最有效方法。从个人开发的角度来看,一份良好的代码规范,能够对代码整洁起到指导帮助作用。从多人协作的角度来看,统一的代码规范能够有效减少沟通的阻碍。

        逻辑清晰

        代码是逻辑的产物。编写代码时,业务相关的逻辑一定要清晰明确,不能模棱两可。除此之外,对于所定义的变量、代码块、数据结构、方法、类、模块等,也要有逻辑地组织它们。

        严谨可靠

        严谨的代码,才能保证它的可靠性,减少bug的发生几率。一份代码即使严格遵守了代码规范,但思考不全面,逻辑不严谨,到处都是各种漏洞和bug,也不能称之为优雅。

为什么要写优雅的代码

        软件工程师有一大箩筐的借口来抵制提升代码质量的要求,更不用说还要编写优雅的代码了。想让我写优雅的代码,没门!

        1、项目进度这么赶,bug这么多,改都改不完,哪有时间写好代码?

        2、代码写得好不好无所谓,也无关紧要,能完成功能,并且不出什么bug就万事大吉了。

        3、上一个人留下的代码就是那么烂,我能怎么办?我也没办法,好无奈啊。

        4、代码写得再好有什么用?公司又不给加薪升职。

        5、公司那么抠,加班那么严重,领导那么挫,还指望我好好写代码?

        ... ...

        这里给出软件工程师必须写优雅的代码的几点理由。

        1、代码首先是写给人看的,其次才是计算机能够运行。

        能完成功能,也就是完成了开发任务,是应该的。但我们的项目是持续迭代的,以前写的代码以后还要去查看和修改,如果每次都只有完成功能的最低要求,日积月累,这个项目所能达到的质量也只会是最低要求,并且这个最低要求还会进一步降低。

        另外,你所编写的代码,在你维护这个项目的期间,你是面对它最长时间的人。写得好,你看起来会舒服,心情也会更好;写得烂,恶心也只会恶心到你自己。

        2、写好代码更能省时间。其实比起写低质量的代码,写出优雅的代码更能节省时间。

        首先,优雅的代码是逻辑清晰的,简单直观的,所以在开发或维护的时候,读逻辑清晰的代码,自然要比读逻辑混乱的代码要更容易,由此就可以把更多的精力与时间花在功能开发上,而不是理清以前逻辑上。

        其次,编写代码时,思维清晰,就可以写出更严谨的代码,这样就能减少bug,也就减少了修复bug所花费的时间。

        不应该把时间都耗费在代码的修复上,而应该更多地用于创造性的工作上。而编写优雅的代码,正是达成这一目标的有效方法。

        3、做一个有所追求的软件工程师,而不是一个得过且过的码农。

        你的代码质量,应该取决于你自己,而不是你的公司、你的领导、产品经理、设计人员,或是项目以前的负责人。有追求的你,不应该让他们成为你降低自己要求的理由。你对自己有所追求,对代码也应当有所追求。

大牛们的话

        关于写优雅的代码这个话题,很多大牛们发表了自己的见解,一起来了解一下吧。

        最好的软件开发人员都知道一个秘密:美的东西比丑的东西创建起来更廉价,也更快捷。而构建、维护一个美的软件系统所花费的时间、金钱都要少于丑的系统。美的系统是灵活、易于理解的,构建、维护它们就是一种快乐。

                                                                                                          —— 《敏捷软件开发》

        代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护和升级奠定了良好基础。 —— 《代码整洁之道》 □ 在代码阅读中说脏话的频率,是衡量代码质量的唯一标准。 —— Robert C. Martin,世界级编程大师,设计模式和敏捷开发先驱 □ 简洁的代码简单直接。简洁的代码如同优美的散文。简洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直接了当的控制语句。

                                                —— Grady Booch,美国Rational软件工程公司的首席科学家

        我喜欢优雅和高效的代码。代码逻辑应该直接了当,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码。性能调制最优,省得引诱别人做没规矩的优化,搞出一堆混乱来。整洁的代码只做好一件事。

                                                                                —— bjarne stroustrup,C++语言发明者

        我可以列出我留意到的简洁代码的所有优点,但其中有一条是根本性的。简洁的代码总是看起来像是某位特别在意它的人写的。几乎没有改进的余地,代码作者几乎什么都想到了,如果你企图改进他,总会回到原点,赞叹某人留给你的代码 —— 全心投入的某人留给你的代码。

                                                                                                        ——《修改代码的艺术》

        如果每个例程都让你感到深合己意,那就是整洁代码。如果代码让编程语言看起来像是专为解决那个问题而存在,就可以称之为漂亮的代码。

                                                                                      —— Ward Cunningham,wiki发明者

如何编写优雅的代码

        1、尽量消除硬编码。

        什么是硬编码,就是说在代码里面,你用的一些变量是写死的。在代码中需要硬编码时,我们要想一想,这个硬编码的值在某些情况下会改变吗,为什么是这个值而不是其他值,确定需要这个硬编码的值吗,等等。当我们思考这些问题后,你会发现,绝大部分硬编码可以转换成常量、宏定义、程序配置等,或者根本不需要。

        2、代码不是越短越好。

        减少代码行数是一个好目标,但是减少阅读代码的时间是一个更好的目标。

assert((!(bucket = findBucket(key))) || !bucket.isOccupied());

        上面的代码只有一行,看似很简洁,但不便于阅读,最好修改为如下代码。

bucket = findBucket(key);
if(bucket != null && !bucket.isOccupied())

        3、注释不是越多越好。

        (1)不要为那些从代码本身就能快速推断的事实写注释。注释一定是表达代码之外的东西,代码可以包含的内容,注释中尽量不要出现。比如,下面示例代码中的注释就是多余的。

class CStudent
{  
public:
   // Constructor
   CStudent();

   // Set the name member to a new value
   void setName(const string &strName);
};

        (2)不要给不好的名字写注释,而应该把名字改好。

// Releases the handle for this key.This doesn't modify the actual registry.
void deleteRegistry(RegistryKey key)

        上面代码中的注释和函数名不是很匹配,应当从源头上修改函数名,而不是添加注释来费力地解释。

void releaseRegistryHandle(registryKey key);

        (3)不要总想着用注释来解决看不懂代码的问题。如果你的代码不易理解,那就应该去改进它,让它能够“自解释”(把命名当做一种注释的方式,让它承载更多的信息),而不是依赖于大段的注释。

        4、适当地注释。

        (1)给重要的语句、解决的bug、关键的逻辑、疑难问题等添加注释。

        (2)给常量、宏定义添加注释。

        (3)给代码中的瑕疵添加注释。

        (4)站在读者的角度写注释。请注释意图(why),而不要注释实现(how),大家都会看代码。

        5、根据逻辑组织代码。

        (1)声明成员变量的时候,通常应当把某个种类,或者某个功能点相关的声明放在一起,并且不同种类或功能点的声明之间应当留有空行。

        (2)声明方法的时候,应当把相关联的方法放在一起,并用空行隔开,而不是按名字排序,或者从上到下一直堆砌。比如:定义了A(), B(), C()三个方法,其中A()调用了C(),那就应该把C()放在A()之下,而把B()放在这两个方法的上面或下面,而不是中间。

        (3)在方法的内部,应当根据具体的业务用空行隔开不同的逻辑,这样不但使代码显得有段落感,也有助于对代码的整体理解。

        (4)在定义类时,相关联的类应互相靠近。同一个库/包里的类应该是共同完成某个模块或功能的,即库/包内的代码应是高内聚的。

        6、命名的原则:正确、准确、直观、简洁。

        (1)正确是指:变量名能够表现它的属性,方法名能够表现它的行为,类名能够表现它的职责,库名/包名能够表现它的功能。

        (2)在做到正确的前提下,再去追求准确。准确是指它的名字是正确并精确的,比如:用手机号是否为空去判断一个用户是否绑定了手机号,方法名isPhoneBounded()就要比isPhoneNotEmpty()要贴切些,尽管它里面的实现是判断手机号是否为空。

        (3)直观是排在简洁前面的,名字长点没关系,只要直观就可以。

        (4)在同样直观的前提下,如果有更简洁的命名,再选用更简洁的命名。命名可以使用缩写,但应该是大家约定俗成的缩写。比如:把message缩写成msg是可以接受的,但把notification缩写为noti或notif,是不太恰当的。

        7、不要把所有变量都定义在开头。

        (1)把所有变量定义在开头是C语言的风格,面向对象语言习惯将变量定义在它开始使用的地方。也就是,什么时候开始使用,什么时候定义。

        (2)用到的时候再定义,既可以缩小变量的作用域,也更符合人的逻辑思维习惯。

        8、提高代码可读性。

        (1)通过提前返回减少嵌套。

if (a) {
    if (b) {
        if (c) {
            // ... 
            return true; 
        }
    }
}

        嵌套太多,不利于阅读和理解,可以修改为如下方式。

if (!a) {
    return false;
}

if (!b) {
    return false;
}

if (!c) {
    return false;
}

// ... 
return true;

        (2)适当地总结变量。

if (A == B) {
}
if (A != B) {
}

        对A == B表达式进行总结,便于后面复用。

bool bRet = A == B;
if (bRet ) {
}
if (!bRet ) {
}

        (3)减少控制流里使用的变量。

boolean done = false;
while (/* condition */ && !done) {
     if (...) {
          done = true;
          continue;
     }
}

        事实上,上面的代码可以不用done变量。

while (/* condition */) {
     if (...) {
          break;
     }
}

        9、提高代码可维护性。

        (1)有互斥锁时,或者有资源创建时,除了对入参的检查可以直接return外,其他地方尽量不要中途return,只在方法的最后进行return。

mutex.lock();

if (!A) {
mutex.unlock();
return -1;
}

if (!B) {
mutex.unlock();
return -2;
}

mutex.unlock();
return 0;

        上面的代码,在每个中途都可能return,此时需要记得释放互斥锁,很容易漏掉,从而导致程序异常。更好的方法是封装自动释放锁的辅助类,或者修改程序结构,不要中途return。

        (2)代码中有多个资源需要动态创建和释放时,可采用do while结构,既便于理解,也不易引入错误。

char *pBuf1 = NULL;
if (A) {
    pBuf1 = new char[256];
}

if (B) {
    delete[] pBuf1;
    return -1;
}

char *pBuf2 = NULL;
if (C) {
    pBuf2 = new char[1024];
}

if (D) {
    delete[] pBuf1;
    delete[] pBuf2;
    return -2;
}

// ...
delete[] pBuf1;
delete[] pBuf2;
return 0;

        上述代码,在每个return的地方,都需要记得释放内存,不利于后期维护,很容易犯错。改为下面的do while结构后,代码变得更为简洁,也更易于维护了。

int nRet = 0;
char *pBuf1 = NULL;
char *pBuf2 = NULL;
do 
{
    if (A) {
        pBuf1 = new char[256];
    }

    if (B) {
        nRet = -1;
        break;
    }

    if (C) {
        pBuf2 = new char[1024];
    }

    if (D) {
        nRet = -2;
        break;
    }
} while (false);

// ...
delete[] pBuf1;
delete[] pBuf2;
return nRet;

        10、一个函数只做一件事。

        (1)编写函数时,记住两条规则。第一条规则是要短小,第二条规则是还要更短小。

        (2)更短小的函数,更易于阅读和理解。如果一个函数的代码过多,很可能是由于你没有对这个函数的功能进行深入思考和进一步分解。函数越大,滋生bug的几率越大,后期维护的成本也越大。

        11、封装。

        (1)时时刻刻将封装牢记在心:将不需要使用者知晓的细节全部隐藏起来,只暴露最少的接口给使用者。

        (2)在各个不同的维度和层次使用封装,设计函数、类、包、库、模块、系统、架构时,都要仔细考虑如何进行封装。

        (3)封装良好的代码,一定是高内聚、低耦合的,便于理解、扩展和维护。

        12、全面思考。

        (1)对一个问题思考得越全面,编写出的代码就会越严谨。

        (2)多反问自己:这个接口被调用多次怎么办?这个接口不被调用,其他地方会崩溃吗?多线程使用这个接口会有问题吗?为什么这里要加锁,那里又不加锁?写了if,没写else,但else真的不用处理吗?… …

总结

  • 平均来说,一次编写的代码会被阅读十次,所以尽力保持代码优雅是有意义的。当养成习惯后,你会发现基本不需要花什么力气,更优雅、更具维护性的代码很快就会产生收益。

  • 优雅的代码有助于理解开发语言、模式和架构,也有利于提升开发水平。

  • 史蒂芬·金在《关于写作》中说,想要成为优秀的作家需要大量的阅读和大量的写作。当人们问Henny Youngman如何能做到在卡内基音乐厅演奏的时候,他的答案是:“练习,练习,再练习。”对于软件开发也是如此:阅读他人优雅的代码,编写代码,不断练习。但很多人忽略了第一步和第三步,总是在不停地编写代码。实际上,第一步和第三步更为重要,它反应了我们借鉴、学习和思考的过程。

  • 墨菲定律中有一条:凡是你认为可能会出错的,它一定会出错。这条定律在软件开发中非常常见,所以写代码时一定要严谨,而不是想着先把功能加好,后面再慢慢去优化重构。记住勒布朗法则:Later equals never(稍后等于永不)。

  • 编写代码,如同写一篇文章。文章的段落划分,如同代码的架构组织。文章的标点符号,如同代码中的空格、空行。文章的措辞修饰,如同代码中的命名。文章的主线情节,如同代码的封装。好的文章,段落划分、标点符号、措辞修饰、主线情节,每一样都是精雕细琢。优雅的代码,亦是如此。

推荐书籍

     

    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/522141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python入门】Python的判断语句(判断语句的嵌套)

前言 📕作者简介:热爱跑步的恒川,致力于C/C、Java、Python等多编程语言,热爱跑步,喜爱音乐的一位博主。 📗本文收录于Python零基础入门系列,本专栏主要内容为Python基础语法、判断、循环语句、函…

Linux高级(shell)

文章目录 一、shell概述Linux 提供的 Shell 解析器有bash 和 sh 的关系Centos 默认的解析器是 bash 二、shell脚本入门脚本格式第一个shell脚本:helloworld.sh 三、变量系统预定义变量自定义变量特殊变量 四、删除变量五、运算符六、条件判断七、流程控制if判断case…

猜谜游戏、彩云词典爬虫、SOCKS5代理的 Go(Golang) 小实践,附带全代码解释

猜谜游戏在编程语言实践都已经和 HelloWord 程序成为必不可少的新手实践环节,毕竟,它能够让我们基本熟悉 for 循环、变量定义、打印、if else 语句等等的使用,当我们基本熟悉该语言基础之后,就要学会其优势方面的程序实践&#xf…

基本类型的比较VS引用类型的比较

基本类型的比较VS引用类型的比较 数据类型的介绍 类型的比较,在Java中有两种数据类型,一种是基本数据类型,一中是引用类型。 基本数据类型包含一些我们常用的数据: 类型类型名称byte字节型int整型char字符型double浮点型boole…

计算机视觉的深度学习 Lecture15:Object Detection 笔记 EECS 498.007/008

一些介绍: amodal box (非模态box?) 标记隐含的物体大小 只检测一个目标的流程 同时计算分类分数和Box坐标、使用Multitask Loss。Multitask Loss现在是一种非常常见的方法。 滑动窗口检测多个目标 开销巨大(右下…

springboot+vue银行OA系统(源码+文档)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的银行OA系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 💕💕作者:风歌&…

声音合成——Foley Sound——DECASE项目——多模态智能感知与应用——Autoencoder代码实现(6)

文章目录 概述encoder的编写过程代码编写运行结果问题总结 decoder的编写过程知识补充关于逆卷积 代码编写运行结果总结 Autoencoder模型编写 compile方法 train方法 保存和加载模型模块编写实现代码——autoencoder代码实现代码——train代码实现代码——保存和加载模型的代…

【半监督学习】Match系列.3

半监督语义分割旨在利用尽可能少的有标注图像以及大量的无标注图像来学得一个较好的分割模型。其中,对有标注图像的学习一般类似于全监督语义分割,如计算预测结果与人工标注之间的交叉熵损失,问题的关键在于如何利用无标注图像。 本文简单介…

【Unity】读写ProjectSettings、UserSettings、Library文件夹中的文件

【Unity】读写ProjectSettings、UserSettings、Library文件夹中的文件 AssetDatabase 类提供的 LoadAssetAtPath 方法和 CreateAsset 方法只能读写Assets、Packages文件夹中的资产,如果想要读写其他文件夹(ProjectSettings、UserSettings、Library等&am…

【运筹优化】元启发式算法详解:模拟退火算法(Simulated Annealing,SA)+ 案例讲解代码实战

文章目录 一、介绍二、基础知识2.1 局部搜索(或蒙特卡罗)算法2.2 Metropolis 算法2.3 模拟退火算法 三、原理3.1 Statistical Equilibrium 统计平衡3.2 Asymptotic Convergence 渐近收敛 四、实际问题4.1 Finite-Time Approximation 有限时间近似4.2 Geometric Cooling 几何冷却…

区块元素和超连结

DIV (区块元素) <div>元素&#xff08;HTML 文件区块元素&#xff09;是无标签语意的容器元素&#xff0c;虽然它不代表任何意义&#xff0c;却是使用最多的标签之一&#xff0c;主要用来把相似或者被划分为同一区块的内容包在同个div 内&#xff0c;以便后续添加css 样…

【MyBatis】一文教你学会使用MyBatis操作数据库

文章目录 1. MyBatis是什么&#xff1f;2. 为什么要学MaBatis&#xff1f;3. MyBatis环境搭建4. MyBatis的使用4.1 简单示例4.2 获取动态参数4.2.1 ${xxx}获取动态参数4.2.2 #{xxx}获取动态参数4.2.3 #{xxx}与${xxx}获取字符串类型数据4.2.4 sql注入问题4.2.5 模糊查询like4.2.…

动态规划-背包问题-背包九讲

title: 动态规划-背包问题 date: 2023-05-12 13:01:31 categories: Algorithm动态规划 tags:动态规划 01背包问题 题目 题目链接&#xff1a;https://www.acwing.com/problem/content/description/2/ 有 N N N 件物品和一个容量是 V V V 的背包。每件物品只能使用一次。 …

数据分析05——往Pandas中导入数据

1、导入Excel&#xff1a; 注意这种方法可以导入xlsx和xls两种类型的数据读入的数据会以DataFrame的格式显示举例&#xff1a;df pd.read_excel(‘./data/demo_03.xlsx’)还可以导入excel文件中具体的某一个表格&#xff1a;pd.read_excel(‘./data/demo_03.xlsx’, sheet_na…

SD-MTSP:麻雀搜索算法SSA求解单仓库多旅行商问题(提供MATLAB代码,可以修改旅行商个数及起点)

一、单仓库多旅行商问题 多旅行商问题&#xff08;Multiple Traveling Salesman Problem, MTSP&#xff09;是著名的旅行商问题&#xff08;Traveling Salesman Problem, TSP&#xff09;的延伸&#xff0c;多旅行商问题定义为&#xff1a;给定一个&#x1d45b;座城市的城市集…

postgresql源码学习(55)—— 列中的NULL值是如何存储和判断的?

问题来自 《PostgreSQL面试题集锦》学习与回答_Hehuyi_In的博客-CSDN博客 第11题 一、 NULL值存储位置 在pg元组头数据中&#xff0c;有一个t_bits数组&#xff0c;用于存储空值位图。当元组中没有null值的时候&#xff0c;t_bits可以被认为是空的&#xff0c;当元组有null值的…

javaweb学习 html+css基础1

1、学习路线 2、web网站的开发模式 课程安排 web 前端 前端学习内容 html和css的帮助网站 HTML 系列教程 (w3school.com.cn) html快速入门 使用VSCode开发工具&#xff0c;输入 &#xff01;enter&#xff0c;即可出现骨架。 ctrl/ 可以将文字转换为注释 右击一个页面…

【JavaEE】计网之IP协议+以太网+DNS

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 网络层重点协议——IP协议&#xff0c;在复杂的网络环境中确定一个合适的路径~ 本篇文章我们来讲解什么是 IP协议及以太网&#xff0c;在IP协议中&#xff0c;有 地址管理 和 路由选择 两个…

C++中基类和派生类的构造函数与析构函数的调用顺序分析

基类为B&#xff0c;派生类为C&#xff0c;直接上代码以及运行结果。 目录 思路分析 代码一 运行结果一 代码二&#xff1a;B(i)改为b(i) 运行结果二 代码三&#xff1a;加上B(i) 运行结果三 代码四&#xff1a;删掉C类定义的B对象b&#xff0c;删除b(i) 运行结果四 思路…

Linux使用全应用

一、CentOS安装Docker Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10&#xff0c; CentOS 7 满足最低内核的要求&#xff0c;所以我们在CentOS 7安装Docker。 基础命令 搜索镜像&#xff1a;docker search mysql 下载镜像&#xff1a;docker p…