代码设计|如何写代码——编程的内功

代码设计|如何写代码——编程的内功

编写代码只是学习一门语言并开始编码吗?看过我的《GoF 设计模式》系列文章的同学或者已经是资深同学的同学显然不这么认为。编程是一项非常严格的工作!虽然我们自嘲是码农,但这份工作毕竟不是真正的砌砖工,我们是软件工程师。编程中需要关注的问题太多了,不仅仅是语言,还有算法、数据结构、编程技巧、编码风格、设计、架构、工程、开发工具、团队合作等方面,涉及到很多层面问题。本文将根据我过去几年的编程经验,分享一些关于如何编写代码的个人见解。

由于部分“跟我来”的朋友编程功底比较薄弱,这里总结一下“编程内功”,帮助他们度过职业生涯的第一个瓶颈期。对了,也会让路过的同学受益!因此这篇文章。

前言

首先想一个问题,什么是编程?编程只是写代码吗?

所谓编程,其实就是不断地为这个现实世界的问题建立模型,并固化成代码自动执行的过程。

~ Bug Hui 《GoF 设计模式 – 解释器模式》

在对问题进行建模的过程中,我们会遇到很多不同层次的问题,所以我们需要很多领域的知识来解决这些问题。

所以编程不仅仅是堆叠代码!

说到这里,我想起了一件事情——为什么业界普遍鄙视半途而废的新人?人和人有很大的区别!我看到训练出来的效果也很好。其实说到底,并不是所有人都被鄙视。就是那些新人,经过几个月的培训,发现只要找工作就能拿到“高薪”,然后觉得编程很简单。因为这段经历给了他们编程如此简单的错觉,我可以训练几个月!有点像刚学会开车的新司机,傲慢地对老司机说:“开车很容易!你看我也可以!”。语言和开发工具只是技巧,是外部技能。而编程思想和经验是内功。这些内功不是仅仅几个月的培训就能掌握的,这有点像中国制造和日本制造的区别。动辄赶上美国也不好。. .

编程不容易!这是一件严肃的事情。但是今天,我不能涵盖所有方面!也就是说,直到今天,我还没有掌握所有领域的知识。所以今天我只是分享一些我在编码本身方面的经验。

另外,本文主要分享如何写代码,而不是如何用Java写代码。因此,文章中可能会出现各种语言。

编码风格

让我们从圆圈中的一段开始。

大多数程序员讨厌工作中的这四件事:

1. 写笔记

2. 编写文档

3.别人不写评论

4.别人不写文件

o(∩_∩)o 哈哈。. 你中枪了吗?

这个片段实际上反映了大多数代码需要大量注释和文档才能将意图传达给维护它的程序员的问题!但是,正如上一段所说,写很多评论和文档实际上是很麻烦的。所以很多时候,因为麻烦而没有写注释和文档,导致维护代码的人相当痛苦。这位苦逼的同学一定经历过!相当于给你一个精密仪器,不给手册就让你维护。

事实上,打破上段描述的恶性循环的一个非常有效的方法是统一编码风格。优秀的代码可以将代码实现为注释,代码本身可以清晰地体现其意图,让他人轻松阅读。这叫做可读性!

姓名

计算机科学中最难的两件事是命名和缓存失效!命名并不简单,它很复杂。一个好的名字可以看出名字的意思,很容易理解。之所以命名难,是因为命名的过程也是概念提取的过程!对问题建模需要提取概念并赋予它们“术语”。这个过程其实是“长征”中最艰难的一步。毕竟无论是设计还是架构,都有成熟的套路可以参考。光是这个过程就是一个创造性的工作,需要程序员充分思考!

以下是总结的一些命名经验:

这个问题怎么说。.

作为程序员,你仍然需要了解基本的英语。否则开发容易遇到天花板,学编程难。毕竟,最新的技术、解决方案和工具都是从国外传下来的。如果是为了解决一些基本问题,每天只做CRUD,看来英文真的没多大用处。但是一旦遇到一些实质性的问题,恐怕只能在英文网站上找到了!ㄟ(▔ ,▔)ㄏ 别告诉我你可以不用堆栈编程。和 from 是终极的编程方法!o(∩_∩)o 这句话才是编程的真谛!(如果你不理解这个梗,你可能是个伪程序员)

其实话说回来,当真的用英文不方便的时候,我觉得也可以用拼音来命名。在这个问题上,你可以务实,做你能做的。但是,拼音和英文混用的做法并不是很好。最好不要!质量不高。

笔记

如何添加代码注释

关于注释,我们需要解决的第一个问题是如何添加代码注释。

对于Java、C#等语言,有专门的文档注释语法,很容易处理。对于 C/C++ 来说,按照约定的格式解释类、函数和代码片段的功能和意图就足够了。至少编译器会执行静态检查。中,对等语言级别的特性有更强大的支持,使用help()来阅读评论非常方便。但是对于像 Lua 这样的弱类型解释语言来说,注解就更难处理了。这是一个使用 Lua 的注释解决方案的示例。

借用 Java 语言文档注释的风格。

文件注释,或类/模块注释。

--[[
    Object-oriented helper functions for Lua
    @author: Elvin Zeng
    @date: 2017-8-21
--]]

功能注释

-[[
    create a class with specified super class.
    if number of parameters is zero, derived class will extends from {}.
    @param superClass super class of target class

    @return derived class
--]]
local function createClass(superClass)
    local derivedClass = {}
    --  省略一堆代码
    return derivedClass
end
--[[
    register a new account
    @param user
      {
        username = "username",
        password = "password"
      }
    @return registered user
--]]
local function register(user)
end

Tips:在Lua中,类和继承可以通过一种机制来实现,类似于通过原型机制实现类和继承。

在便条上写什么

我们先来看一个反例。

/**
 * 查询
 */
public List
queryPage(int pageSize, int pageIndex) throws PageIndexOutOfBoundsException { // 定义一个整型变量 int offset; // 省略一堆代码 }

首先,方法名本身不好。先不说这个,先说一下注解的问题。这里的评论有几个错误:

1.方法被注释为“查询”,简直是废话!方法名已经告诉别人这是一个查询方法。在这个评论中写这两个词有什么意义?最后查询这里没有说什么!

2. 参数不加注释。各参数的含义和取值范围不作说明!

3、没有明确说明什么情况下会抛出。

4. 不要写“定义一个整数变量”之类的垃圾注释。这么简单的一句话谁看不懂!如果要评论,也写下这个变量的含义。

这里我们不考虑设计问题(分页索引号最好自己调整到合理的值),我们来看看改进注释后的代码。

/**
 * 列出指定分页的文章
 * @param pageSize 分页大小。如果等于0则表示查询出所有文章。
 * @param pageIndex 分页索引号。必须为一个大于0的整数,第一页索引为1。

 * @return 指定分页的文章列表
 * @throws PageIndexOutOfBoundsException 当分页索引号超出正常范围时抛出,即pageIndex小于0或大于最大页索引时。
 */
public List
listArticle(int pageSize, int pageIndex) throws PageIndexOutOfBoundsException{ // 第一条文章记录在MySql数据库中的偏移量 int offset; // 省略一堆代码 }

做完修改后的评论,感觉资料完整多了!虽然代码本身就是最好的注释,但必要的注释还是要写的。毕竟在调用的时候,没人能猜到你的索引号是从0开始还是从1开始的函数里面直接对函数注释。代码是写给别人调用的。如果没有基本的注释信息,那么每次调用你的代码,都要看你的函数中的具体逻辑,才知道怎么调用。这显然是非常低效的!

如果命名和注释这两个基本方面没有做好,就会影响整个团队的运作。也就是说,你封装的东西并没有为队友节省多少时间,而且当别人使用你的代码时代码设计,阅读你的代码也需要一些时间。如果团队中的每个人都这样,整个团队将极其低效。就个人而言,我非常不愿意与编码风格如此糟糕的人一起工作。

参考规格

关于编码风格,本文只谈命名和注解。其他问题如缩进、空格、换行、空行等,可以参考本节给出的参考规范。

不同的公司会有不同的编码标准,所以没有办法给出适合所有公司的标准。但是,在制定自己团队的规范时,可以参考一些大企业的做法。以下是全球最大的互联网公司谷歌的编码标准。同学们可以参考一下。

异常处理

异常和返回值有什么区别

在 C 语言中,我们的函数通常会返回一个整数值作为状态码,以通知客户端调用结果。例如,0 表示成功,非 0 表示失败。并且可以用不同的值来表示不同原因引起的故障。但是在Java、C#、C++等面向对象的语言中,一般不使用返回值来表示状态。返回值一般用于表示返回的业务值,异常用于通知客户端程序运行状态发生了变化。

什么时候需要抛出异常

关于这个问题,我想到了一个很简洁的句子:当函数不能完成其声明的任务时抛出异常!

比如上面一天,当方法由于各种原因无法查询到文章列表时,就会抛出异常。

在这种情况下,抛出异常是非常必要的,因为当别人调用你的代码时,你可以放心地调用它。只要调用你的方法,就会返回文章列表。如果无法返回文章列表,则会抛出异常。调用该函数时无需怀疑是否执行成功。

更明智的说法是:

我宁愿终止程序,也不愿在出错时运行它。

换句话说,当遇到错误时,最好抛出异常来终止程序,而不是错误地运行它。这是在偷钟!

异常需要携带什么信息

首先,异常的类型本身会携带异常类型信息。其次,异常的属性可以携带更详细的信息。这里要小心,不要像下面这样。

抛出新的(“失败!”)

如果抛出异常,则执行一定失败!带这种信息有什么用,不是废话吗!

应该是这样的

“` throw new (“参数页索引号不能大于总页数”);

另外,异常栈也承载了很多信息。

日志

说到日志,我们首先要明确一个问题,日志是干什么用的?

用于记录运行时错误信息!

是的。好像大家都知道日志是干什么用的代码设计,但是为什么写代码的时候忘记了初衷!

让我们看一下代码:

/**

这里的代码是什么意思?程序员应该能看懂!显然,程序员想要使用这些标志进行调试,并且想要知道执行了哪一行代码。然而,这里显然犯了两个错误。

为什么 .out.(“”); 而不是 .debug(“”);?

为什么是 1、2 而不是一些更明确的文本信息?

这里,合理的方式如下。

/**

提醒一下犯上述错误的同学:

使用日志框架并在适当的级别输出日志很重要。

许多程序员从未负责或参与过运维相关的工作,甚至在Web上工作了几年,也从未发布过自己的网站。所以根本没有后期维护的意识!

没有这些日志,项目上线时,运维的支持者只能在发现网站宕机后直接重启网站,然后当成没事。因为没有排除故障的线索。

输出有效信息。

不要输出1、2、3、、、hello等无意义的日志,输出.debug(“邮件发送任务成功入队。Task id:” + );等有效信息

也许当你在调试时,这些奇怪的字符串对你来说是有意义的,但对其他人来说,这些都是圣经。运维靠山来拿刀砍你!另外,不要输出“——–开始执行——–”之类的日志,在运行过程中定位问题没有任何优势!可以自己使用,提交代码前必须删除。

将上下文信息带入日志。

错误日志中的孤立句子通常没有什么实际用途。比如上面的例子中,如果在找不到指定的模板文件的情况下,发送邮件时指定的模板文件名没有输出,那么在排查时就无法知道是哪个模板文件丢失了。

不要在日志中输出敏感的用户信息。

切勿在日志中输出用户密码、邮箱内容等与用户隐私相关的敏感信息,也不要输出验证码值等敏感信息。

参数验证

在您向公众公开的方法之前插入一些参数检查代码,以确保以“正确的方式”调用该方法。例如:

/**

// MySql 数据库中记录的第一篇文章的偏移量

诠释;

抛出新的(“”);

// 省略一堆代码

}

参数校验的作用

如果在暴露给外界的重要方法的开头不插入代码验证参数,有时方法需要在方法内部深入运行才能抛出异常。在这种情况下,可能会抛出各种异常。比如空指针、被零除异常等。

这种情况下,很难一眼看出这个异常的来源是传错了参数。您需要稍微调试一下您的代码!如果把校验参数的代码从头插入到代码的入口处,那么在调用的时候,一眼就可以看出参数传错了,导致了异常。这样,其他程序员在看到异常时会查看您的方法注释。他看着,哦!原来分页索引号是从1开始算的,所以这个问题就到此为止,为团队节省时间。

参数验证问题是影响团队效率的关键因素。所以,请同学们注意这个问题。我们都是工程师,我们作为一个团队工作。自己写代码不快,但是整个团队都快,真的快!很好地使用断言可以使您的代码更加健壮。

Tips:Java中默认的断言是没有开启的,所以建议忽略Java语言的断言,自己处理。

什么时候需要参数验证?

我认为当满足以下条件时,方法或函数有必要进行参数验证:

1. 方法或函数是公共的,而不是私有的。

2.当参数可能是空指针。

3.当参数的合理值无法通过方法名、参数名、参数类型一目了然时!比如上面那个是从1开始数的,但是别人不知道你是从1开始数的。

如果每种方法都验证了,其实还是挺麻烦的。程序员的时间很宝贵,没有那么多空闲时间。但是,如果满足上述条件,最好检查一下。因为做这个验证,你自己会浪费几分钟的时间,但是从团队整体的角度来看,总的调试时间减少了。请记住,方法/函数是为其他人调用而编写的!

需要做多少参数验证

我有个标准,就是把自己当成调用这段代码的人,把自己当成任何“姿势”都可能被骂的菜鸟(其实也可能是一个不叫的大佬)理解你的代码)。如果这个时候你也可能会犯一些错误(比如不注意边界值,不注意是否可以为空),那么这个时候一定要进行验证。对于一些不太可能出现其他层处理过的错误的情况,可以不进行验证。例如,您有一个签名为 void(用户用户)的方法用于注册用户。这种情况下只能检查用户参数是否为空,不检查用户的属性(用户名和密码的长度是否合法等)。因为你在绑定上层控制器的模型的时候已经做了非常严格的验证。当然,如果你在这里有足够的时间,也可以去看看。具体做多少取决于你的情况。

后记

编码标准是用来约束别人的!

o(∩_∩)o 哈哈!只是在开玩笑!

其实很多时候,出于各种原因,比如“项目周期比较紧”、“项目可行性还在探索阶段,可行性未知,会先实施”、“其他代码在项目已经这样了,罐子坏了”等等。结果可能是我们这些自称“经验丰富”的程序员可能写不出完全符合这些理想的代码。也许是吧!

ㄟ(▔ ,▔)ㄏ

我承认,我也写过奇怪的代码。

然而,这似乎不是你,未来优秀的程序员,不应该咄咄逼人的原因。

小时候,老师教我们要诚实,但老师自己未必能完全做到。我们可以为此鄙视他。

长大后,我在生活中经历了很多无奈,不再鄙视“不诚实”的老师。即使他低下高贵的头,他也变成了这样。

以后,你还会教子孙“老实”吗?

恐怕会!

因为,优秀的创意,无论结果如何,都应该被推广!

本文中的观点只代表我现在,人会成长,明天我可能会有新的见解!如果对部分观点不认同或者有其他优秀的想法,可以给我留言。我们一起成长!

相关推荐

成考大专能不能落户长沙,成人高考可以落户吗

有不少成考本科生问“成考本科可以落户长沙吗?”针对这一问题湖南自考网www.zika...

自考本科专业选择建议,自考的专业层次怎么选

首先将有学历提升需求的人群大致划分为3类:1、转行/求职,寻求一份新领域的工作2、升...

安徽省教育招生考试院高考答案,安徽高考模拟题

据安徽省教育招生考试院消息6月6日,安徽省教育招生考试院发布2022年普通高考温馨提...

Magento 系统中的 12 种代码设计模式

早期框架中的比较出色的一点是,其中的绝大部分(甚至所有)使用的设计模式的方式都有其用...

6月4日自考报名开始!山西省将首次使用在线平台进行注册和支付!

hello,大家好!今年的自考报名终于要开始了!提醒同学不要错过报名时间教育部也发布...