#前言
本文试图通过介绍编程工作中的部分技能和知识,帮助新进入程序员职业的朋友熟悉未来的工作。特别的,本文将额外提供以 Java 编程语言下的 Spring 框架开发 Web 应用后端逻辑的工作相关的介绍。
因本文实在冗长,建议你结合目录挑选自己感兴趣的部分阅读。如果你想通过文本做跳板寻找优质学习资源,希望附录部分我精心挑选的网站和书籍推荐让你满意。
对读者的要求:打算把程序员作为职业,还尚未入行的朋友。
#工具
#IDE 与文本编辑器
文件格式分两种,人能读懂的文本文件,和机器能读懂的二进制文件。源代码是文本文件。
编程其实是在用文本编辑器(Text Editor)修改一个个文本文件(源代码文件)的内容,就像你用 Windows 记事本打开 txt 文件敲一些文字一样。理论上你可以用记事本写源码,实际上,有一些电脑达人们喜欢用 Linux 下的Vim和Emacs写源码,它们相当原始,但若用好快捷键与定制功能,它们会变得非常强大。
虽然可以,但是你往往不想用记事本写代码,因为它们不是“专业写代码”的工具软件,用起来不顺手。你需要的是专业帮手——IDE。IDE 全称集成开发环境(Integrated Development Environment),是用于提供程序开发环境的应用程序,包括代码编辑器、编译器、调试器等工具,集成了代码编写、语法分析、编译、调试等额外功能。IDE 本质上是一个挂载了许多协助编程的额外功能的文本编辑器。
一款,你只需要选择并熟练使用一款文本编辑器,你便有了编写源代码的趁手工具。无所谓它是否属于 IDE,自己喜欢就好,因为那些编译调试之类的工具大可以在文本编辑器外单独使用。不同文本编辑器的基本功能(即文本编辑功能)大同小异,我们在意的是附加的功能(或丰富的插件市场)、以及完备的快捷键集合(快捷键对程序员来说很重要,可以让我们手不离开键盘完成代码编写,时不时抓鼠标会减慢速度且打断思路)。
我目前用了三款文本编辑器以适应不同需求。下面我给出一些我知晓的文本编辑器供你浏览,你也可以自行搜索"editor for programming":
- Vim和Emacs 这两个文本编辑器很特殊,你可能会在任意一款热门编辑器上看到这两个编辑器的快捷键替换插件。它们的狂热用户们(也同样是业内的老前辈、扛把子们)不愿意因切换文本编辑器而学习新的快捷键,因为快捷键已经变成了某种肌肉记忆,很难改变。这时你应该注意到了,快捷键是某种门派一样的东西,同时学两套快捷键会形成冲突,你只能选一套并把它变成肌肉记忆。快捷键会伴你整个职业生涯,因此慎重选择你的文本编辑器(或者直接学这两款文本编辑器之一的快捷键)。
-
轻量级:
-
Notepad++ 它有一些用户插件来提供额外功能。因为它的启动速度很快,我用它作为最轻量级的文本编辑器,用来临时记录或粘贴一些文本。
-
Sublime Text 大概是和 notepad++并列的一款轻量级文本编辑器,我没用过,我有一个朋友把它作为轻量编辑器使用。
-
-
中量级:
-
Visual Studio Code 简称 vscode,中量级的全能文本编辑器(全能指什么编程语言都支持),有丰富的插件市场,你可以把它改造成编写任何编程语言的 IDE。我最近发现自己修改格式化的配置文本文件的频率变高了,因此选择了它作为中量级的编辑器,用它阅读与修改 markdown(现在正在用它写这篇文章)、json、toml、yaml 等格式的文件。事实上许多前端开发者使用 vscode 作为编写经典前端代码(HTML+CSS+JavaScript)的 IDE。
-
Atom 大概是与 vscode 相对的中量级编辑器,还是我的那个朋友会用它,我个人并不了解,姑且列出来。
-
-
重量级(IDE):
-
Visual Studio 微软出品,商业产品,被戏称为宇宙第一 IDE(但我没搜到原因)。据我所知可以编写.Net、C、C#、C++、Python、Android 语言。体积超级大,启动超级慢,用起来…我感觉不到好在哪里,可能我没有深度使用。有插件市场。当你看到安装界面中各种数据分析、机器学习等高大上的可选项时,你会感受到这 IDE 真的体现了微软一如既往的商业又专业的味道。
-
JetBrains IDEs JetBrains 是一家公司,它旗下有诸多用于编写不同语言程序的 IDE:IntelliJ IDEA,GoLand,PyCharm,WebStorm,RubyMine…用来编写 Java、Go、Python、JavaScript、Ruby 等主流编程语言。这些 IDE 基本共用一套快捷键。有插件市场。我是 JetBrains 的忠实用户,因为我感觉 JetBrains 的 IDE 开发者们懂得开发者需要什么,产品中的小细节和功能体现着人性化的用心。我用 JetBrains IDEs 作为重量级编辑器,因为它们启动要好一会。我还选择了 JetBrain IDEs 快捷键“门派”,还在 vscode 上安装了"IntelliJ IDEA Keybindings"插件。
-
Eclipse IDE 开源的多编程语言 IDE,有插件市场。因为它是开源的,有不少商业第三方定制化 IDE 都是基于它改造的。我在大学时一直用它编写 Java 和C++,真的很不错,直到我在实习时试着换了 IntelliJ IDEA,之后再也没启动过它。
-
Android Studio 谷歌出品,看名字就知道是编写安卓系统应用程序的 IDE。我用过一点,只知道它因为时不时要连接谷歌的网站更新索引之类的什么东西,在国内想用它要配置好离线依赖。另外关于它有许多 meme 损它启动慢吃内存多,比如:我是安卓工程师,我每天早晨来公司先启动 Android Studio,然后把咖啡壶放在机箱上煮咖啡,等我喝完整壶咖啡,我就可以开始工作了。
-
Xcode 这是苹果公司推出的编写苹果各产品上的应用(现在苹果产品有了共用芯片,只需编译出一种应用)的 IDE,我没有用过,也没有用它的苹果开发者朋友,所以我不知道它怎么样。按照苹果一贯的风格,也许这个 IDE 很顺滑,搜了下也没找到太多说 xcode 本身不好用的抱怨。
-
#Java IDE
主流 Java IDE 有两款,Eclipse IDE和JetBrains IntelliJ IDEA,而且从2018年开始,Visual Studio也能够(通过连接 vscode)支持 Java 了。从上文你可以看出,我是属于 IDEA 派的。
在我看来,Eclipse 比不过 IntelliJ IDEA 的关键不在功能上,它快捷键和插件市场都很完备,甚至因为是开源的,所以你可以把它 DIY 成符合自己习惯的专属 IDE。但它败也败在开源上:没有漂亮又符合人机交互理念的界面(这需要专业的 UI 设计师参与,而开源项目成员中往往只有程序员);默认配置较不合适(可定制化和开箱即用不冲突,但需要认真琢磨出厂默认配置)…细节,它败在了细节上,“The devil is in the detail”。
截至撰文我已经快三年没有用过 Eclipse 了,因此不了解它的近况,也许你可以给它一个机会,试用一下?还有宇宙第一 IDE…也可以试一下,我猜配置会很麻烦。
另外,如果你选择使用 IDEA,IDEA 从2016年开始内置了一个学习快捷键的插件 Learn,可以看这篇官方介绍学习如何使用。
#浏览器、搜索引擎与搜索思维
#浏览器
你在用火狐(FireFox)浏览器吗?建议从 Mozilla 官网(https://www.mozilla.org/zh-CN/firefox/new/ )下载,而非北京谋智火狐信息技术有限公司的网站(http://www.firefox.com.cn/ ),这两个的区别可以看此文章。
你必然已会使用浏览器和搜索引擎,我在此补充几个浏览器的别样用法,也许可以帮助你更好的使用浏览器:
-
给 PDF 阅读材料加书签。现代浏览器内置了 PDF 阅读器,配合创建用读书笔记作为标题的书签,你可以为本地硬盘上的某个 PDF 格式的电子书建立自己的书签目录,方便以后连同互联网书签一同查阅。(遗留问题:那个 PDF 文件不方便随意更换位置,因为浏览器书签的地址是文件系统的路径)
-
插件市场。主流浏览器都有自己的插件市场,你可以用插件让上网体验更好。值得一提的是Tampermonkey插件,它是 JavaScript 脚本管理器,有自己的脚本市场。你可以把脚本理解为轻量的插件,毕竟在浏览器中就是 Js 脚本在操作页面。通过 Tampermonkey,你可以向某个网页中添加额外的脚本来实现额外功能,而浏览器插件可以为浏览器本身添加额外功能。
-
书签功能。主流浏览器支持同时打开一个书签目录下的所有书签,这样你可以用一个目录来代表一个工作环境,需要时一键开启所有 Tab。
-
浏览器快捷键。页面内查找,Tab 左右切换显示,将光标置入搜索栏、切换搜索所使用的搜索引擎…
-
搜索栏。主流浏览器的搜索栏可以同时搜索书签和历史记录或使用搜索引擎。
-
历史记录。历史记录很重要,虽然它会泄露隐私,但我从不清理它。只要有历史记录,我就能搜到曾经浏览过的网页,节省了重新搜索的时间。(如果我想进行隐私上网,我会打开虚拟机中的 Tor 浏览器…)
#搜索引擎
为什么我不使用百度?有几个原因,仅就编程方面的搜索来说,使用百度会搜索到大量内容农场网站,浪费时间却找不到有用的信息。
我使用微软的 Bing( https://cn.bing.com/ 这是国内特供版网址,Bing 的通常网址是 https://www.bing.com/ ,猜测微软是根据 GeoIP 来判断该重定向到哪一个网址)。Bing 搜索引擎有一些简单的高级搜索语法。
另外 Bing 有两个版面:国内版和国际版,你可以简单理解为国内版搜索结果中的中文结果优先,而国际版是其他语言的结果优先。你可以轻松切换两个版面来实现同时搜索一个关键字的中文和英文搜索结果。就编程方面的搜索来说,你不能肯定一个问题在CSDN或博客园上没人写过踩坑文章,也不能肯定StackOverflow上必然有现成的回答。既然我们会中文和英文两种语言,就应该利用这种优势,同时使用两种语言搜索同一个问题(用中文关键字搜中文结果,vice versa),这样找到答案的概率会更高。
用 Bing 搜索中文编程内容能逃避内容农场吗?不完全能。但是因为百度和 Bing 是两个公司单独计算网页排名,而刷排名的坏东西的目标一般是百度排名,因此 Bing 比百度的情况好一些。事实上不止中文搜索结果有内容农场这个坏现象,几乎所有语言的搜索结果都有,这需要搜索引擎公司们自己想办法反制。
#搜索思想
记得把使用搜索引擎查找互联网内容的思想迁移到各个使用场景上。想找存在电脑上的文件?Windows 系统可以按一下 Win 键打开搜索栏。想在网页内找关键字?使用浏览器的网页内搜索。想在文本编辑器里搜索本文件中的内容?文件内搜索。在 IDE 里搜索整个项目目录的内容?项目内搜索。
小提示,本文中所有超链接标注的名词都可以作为搜索关键字,我只是每个名词搜了一个质量差不多的网页附上链接…多花点时间应该能搜到质量更好的。
搜索的最大目的始终不变:节约寻找答案的时间,因为寻找答案过程本身没有产出。
#编程语言
文本命令:“老王 吃 苹果” -> 执行动作:老王在客厅桌子上找到一个苹果,吃掉。
编程分为两个部分,人编写文本格式的源代码,编译器|解释器(下面统一使用编译器做指代,即使实际上是解释器)负责将源码编译成机器能看懂的指令。你可以为任何有结构的文本编写一个编译器,文本就是命令的内容,简单说明了要做什么事,编译器按着文本写出详细指令,计算机按着详细指令做事。举个例子:打仗,司令部传达过来的命令很简单,然后战场上的指挥官会以一两行命令为大纲来拟定详细的计划,士兵们可以按照详细计划行动。
编译器和战场指挥官一样,帮你补足你在源码中没有提到的细节。注意看上文那句老王吃苹果,源码里没有提到老王在吃苹果前需要找到苹果,也没提到如何找苹果,但最后执行的动作中是存在找苹果的步骤的。这引出了目前主流的两类编程范式:命令式(imperative)和声明式(declarative),按命令式编程设计的编程语言需要指明执行的细节,比较唠叨,而声明式只需要指定要做什么事,细节由编译器补足。事实上现代语言都混合了不同的编程范式,更加灵活。上面老王吃苹果的例子是偏向于声明式的。
依我看,编译器越“聪明”,越懂得从寥寥数行源代码里猜测程序员的意图并产生出程序(如 JavaScript,尽一切努力不报编译错误,即使会输出很离谱的结果);反之,编译器越“木头脑袋”,越要求源代码“符合语法语义规则”,源代码越冗长(Java,说的就是你)。要么人写源码多费功夫,要么编译器编译源码多费功夫,仅此而已。编译这门手艺的术语叫编译原理。如果你有一个无限智能的编译器,就可以用自然语言即中文英文等,去指挥计算机做事。可惜,编译器是有极限的,语言越复杂,编译器所要做的工作越多,人就越难去编写这个编译器(编译器是个特殊的程序,终究还是要人来写),最后我们折衷得到了目前这些编程语言和对应的编译器。
不同编程语言适合做不同的事,就像木匠工具箱里不同工具用来做不同的工作,没有高下之分,也没必要搞工具崇拜。因为有些编程语言底层设计特性是相互冲突的,而且现在已有这么多用得好好的不同语言编写的程序支撑着我们的世界,所以从设计理论和改造成本上看永远不会有一个全能的编程语言可以代替所有语言。顺带一提,每个编程语言的命令表达能力都是相等的,因为它们都是图灵完备的,所以“一个语言可以代替另一个语言完成某个具体工作”这种论断是正确的,只是用不同语言间写源码完成相同的某功能花的力气可能差个几倍几十倍都有可能。
编程语言本身,和编程语言下的框架、第三方库,只是工具而已。它们一般有个说明书教你如何使用,你可以在网上搜索(官方教程或注释生成的 API 文档)。有时你能在 IDE 里看它们源码的注释,注释也会详细地解释这个代码要做什么,就不用搜索了。事实上,在商业编程刚刚起步时,每个程序员手边都有一本关于所用编程语言提供的库 API 与语法的手册,他们要像查字典一样使用这本工具书,因为那个时候没有互联网,没有在线文档。不要过多花时间在了解工具的使用上(通读文档,试图记住所有库 API,类似背诵字典),有需要就用关键字去搜索,越用越熟,就能记住了。
#学一门编程语言
自然语言有听说读写四个技能要求,编程语言是书面语言,学习一门编程语言仅要求读写两个方面,另外额外多一个:
- 学习它的编程思想、语法和关键字,即读法写法。
- 知道语言的原生库提供了哪些 API(帮你省事的现成的功能),知道如何使用或去哪查如何使用这些 API(小提示:库代码的注释,网上的在线文档,或教程)。
推荐一个入门图书系列——Head First。由 O’Relly 出版社发行的一系列教育书籍,它强调以特殊的方式排版,由大量的图片和有趣的内容组合构成,而达到非疲劳的沉浸式学习效果。我读过它的设计模式书,很有趣。
还有几个在线中文教程网站:
-
阮一峰的网络日志,廖雪峰的官方网站 这两位为中文互联网提供了大量易懂的入门级 IT 方面的教程,如果你想通过中文教程了解一个新东西,可以先在搜索栏前面输上"阮一峰" “廖雪峰”+新东西关键词,看他俩有没有写过。
-
菜鸟教程 特别的,如果你想通过中文教程学习某种编程语言|工具,并快速写出能跑的程序|运行环境,建议阅读这个网站上的教程,这上面基本涵盖了所有常用的编程语言和工具的快速入门教程。
#Java 编程语言
Java 中规中矩,有一套严格的语法规则。通过学习复杂的语法规则,新手可以了解编程语言的一些共性,我还是比较推荐新手通过 Java 了解面向对象编程的。最近几年 Java 想要变年轻,迭代变快了(半年一个新版本),也引入了不少其他语言中好用的特性。你可以把它理解为保守谨慎的,只会在一个特性被其他语言实践验证后才考虑引入。但是业内不少公司都停留在了 Java8,主要是公司们不想还遗产代码的债。
如果你工作项目使用的是 Java8,推荐你了解一下 Java8引入的Stream API。如果你需要 Java 语言的工具书,建议使用Java 核心技术,上下两卷两本大厚书,你也可以通过通读它来学习 Java。
#第三方库
在现代,没有任何编程项目是从零开始的。每个编程语言都有自己的第三方库仓库(repository),比如 Python 的pypi,Java 的Maven Repository等。有些公司为了保证项目所使用的第三方库的稳定,会建立并维护内部的仓库。库(library)或者叫包(package),就是别人写好的功能。你可以把库从在线的仓库下载到本地,就不需要自己编写了对应的功能了。这算盗窃吗?事实上,每个库(每个开源软件项目,库属于一种特殊的专门给程序员用的项目)都有许可证,许可证是一个声明,它规定了该库可在什么情况下使用,比如有些许可证就禁止商业使用。
#框架
框架(framework)是一种特殊的项目,顾名思义,它提供了编写某类程序的大致框架。当你自己动手写练手的项目,你会发现写项目最难的在于如何组织各个组件间的关系,我该把这个文件放在哪个目录?我该把这段逻辑放在哪个组件?谁调用谁?选择一款框架,了解它关于组织组件的建议,你便只需专注你的功能。
#架构
架构(architecture)与框架的联系与区别,微服务架构和单体架构。如果想了解更多,请自行搜索。
#Java 下的 Spring 框架
“Spring 框架(官方网站)是最受欢迎的企业级 Java 应用程序开发框架。” 听听,它可没吹牛。目前 Spring 已经发展成了完整的生态(我不喜欢这个词,它代表封闭,但 Spring 是很开放的),绝大部分新的 Java 应用项目选择 Spring 生态作为基础框架是相当自然的事。
使用 Spring 对程序员编程有很多实际的好处,不仅是企业级的那些什么可伸缩之类的高大上特点:
- Spring 有完备的基础功能,可以让你只写几行代码就配置好一个复杂的功能,如果不用 Spring,也许需要一整天去下载配置相同功能的库或其他外部系统。
- Spring 开箱即用,简化了项目配置的过程(配置真是一个消磨精力的事)。
- Spring 统一了 Java 业界的标准,你会发现各种项目和库都支持对 Spring 的兼容,不需要你来单独做兼容了。
- Spring 对原本代码的侵入相当少。
Spring 增加了学习成本。我已经见过好几个人,当他们听到除了学 Java 还要学 Spring 时,眉头突然皱起来了…但是…总要多学一些工作中会用到的常用库,只是 Spring 稍微有点大,稍微。而且你这样想,学了 Spring,就能享受上面提到的大量好处,很值。最低限度的学习,只需要了解 Spring 配置文件和 Spring MVC 的注解就行了。
#工具库
按照我的分类,工具库通过 API 提供对象处理功能,你可以像使用语言本身的原生库 API 一样使用这些工具库。举个例子,因为 Web 应用的普及,每个语言都会遇到对Json 格式文本的解析、映射等操作的功能需求。比如 Java 语言没有在其原生库中提供这些 Json 格式数据的操作,于是 Java 社区中应需求出现了好几个 Json 处理工具库。
#Java 常用的工具库
-
数据处理(狭义上的工具类):HuTool,apache commons-lang3
每一类有其中之一就满足需要了(slf4j除外)。如果你中途加入一个项目组,你需要辨别该项目使用了哪些工具库,并继续使用,不要添加相同功能的其他工具库。
#依赖管理工具
当你使用某个第三方库或框架,你便对它产生了“依赖”,有时我们把它名词化,项目依赖的第三方库就起个别名叫“依赖”(dependency)。当然你使用的第三方库本身也可能有依赖,于是会形成一个以你的项目为根的依赖树。
依赖管理工具 源自人们想要自动化(程序员爱自动化)依赖管理的需求。一般依赖管理工具和第三方库仓库是相辅相成的,本地的依赖管理工具会将第三方库仓库的网址作为远程仓库,从上面下载第三方库文件。部分依赖管理工具还拥有工程构建的功能,你简单理解为编译、打包与部署项目。
#Java 依赖管理工具
主流的 Java 依赖管理工具有两个:Apache Maven和Gradle。区别?看这篇中文文章或者这篇 Gradle 自己写的对比或者这篇知乎回答。
#往项目里添加依赖
以 Java 语言项目为例,单个现代项目往往被分成多个包(package,对,就是上面提到的第三方库的一种别名),包之间的有一定的隔离性,依赖也是各个包独立管理的。
在编程工作中,你会碰到想使用某个第三方库(或者同一个项目的另一个包)的功能,而目前项目里还没有这个依赖的情况。此时你应该斟酌一下你想在哪个包的代码中使用第三方库,对应要把新的依赖放在哪个包的依赖配置文件里。这个问题没有固定答案,原则就是尽可能少增加复杂度。如果你拿不准,咨询一下有经验的程序员的意见。
有一个编程原则叫“针对抽象而非具体编程”,我们应该尽可能减少程序中不必要的依赖,因为它会妨碍未来的变更的灵活性。反面例子:依赖是一个树型结构,有些库没有好好遵守这个原则,依赖于某个具体的库,导致使用该库的其他库也传递依赖了那个具体库。结果越来越少的库为了避免麻烦选择使用这些库,最终维护者因无人使用放弃了维护,项目死亡了。不可在未来灵活替换的依赖对项目来说是致命的病毒。
#单元测试与 Debug
关于单元测试的定义与用途,读百度百科的介绍。关于单元测试思想方面的内容,可以读这篇文章。简单说,单元测试是程序员为了检测自己写的功能是否合乎预期而编写的额外的代码。把这种测试的行为用代码持久化下来有不少好处,比如可以重复运行。单元测试不是必须的,写它可能要费不小的事,所以有时要斟酌是否值得写单元测试,主要看写测试的时间能否弥补未来一遍遍手动测试的时间。
一般使用现成的单元测试库来编写单元测试用例。单元测试库属于工具库,也是百花齐放多种多样。现代的框架,比如Java 的 Spring,Java 的 Project Reactor(我相信其他语言的编程框架也有,奈何我没用过无法举例)都会比较贴心地随框架附带专用的测试工具包,帮助你更顺利地使用框架。如果一些热门的库本身没有提供测试工具包,也会有热心程序员提供第三方的辅助测试包…社区就是这个样子,有需求就往往有对应的现成解决方案。
单元测试库的 API 是额外的学习成本,所以部分程序员不喜欢写单元测试,或者写的单元测试无法自动化运行,需要人盯着看日志输出结果。还有一种不写单元测试的情况是,不知道怎么写、或者没有单元测试库的依赖(内网开发时会遇到)或项目使用特殊的商业框架,因此有心无力写不出单元测试。对于前者,斟酌一下学习并写出单元测试的成本;对后两者…默哀,放弃单元测试吧,争取用其他工具把集成测试的测试用例自动化。
Debug,或者叫调试,是在程序尚未被打包部署前,把程序在本地的环境里通过调试器等工具的帮助下运行起来,细致观察程序内部的每一步行为,以确定程序行为符合预期。现代 IDE 中一般会集成调试器和 Debug 功能,因此通常的实践是在 IDE 中,以 debug 模式运行单元测试用例或整个程序。具体怎么用要查 IDE 的官网文档或自己动手试用,别忘了学习对应的快捷键。
#Java 的单元测试包和 IDEA 的 Debug 功能
我会介绍我现在用的测试库,你也可以选择自己整理一套喜欢的测试库。自从我学会借助测试库写单元测试后只用过两种语言:Java 和 JavaScript。写 JavaScript 单元测试我是用 FaceBook 开源的Jest,本身功能就挺全的,有时会安装基于它的插件包来扩展功能。
Java 就复杂点了,我现在有一套会用的 Java 测试库,搭配使用它们可以完成基于 Spring 框架的应用的各种单元测试工作。关键是它们可以用一个spring-boot-starter-test依赖全部导进来,省事(而且公司的内部仓库里大概率会有)。主要有这么几个包:
- Junit 它是一个测试框架,很多其他测试包会基于它编写,角色类似上面的 Jest。公司项目中有些用 Junit4,但它比较老(10年以上),现在出了 Junit5,注意两者间用法的不同(@Runwith 兼容,Mockito in Junit5)。
- Spring 提供的测试组件
- Spring Boot 提供的测试组件
- Mockito Mock,嘲弄,在测试中我们说 mock 被测试组件的依赖,是指把调用被试和被被试调用的组件,全部用测试库提供的 API 换成假的空壳子。这样我们可以控制输入被试的数据,和被试调用其他组件时其他组件的反应。举个例子,我们可以 mock 一个数据库 API,然后写明如果被试调用该 API,抛出异常,测试被试在数据库异常时的异常处理逻辑。
- Hamcrest 小巧的断言(assertion)框架,作为 Junit 断言 API 的替换品,提供可读性更高的 API。(Hamcrest 仅是判断一个对象的值怎样怎样,如果想判断其他的比如抛出异常,用 Junit 的 API 更方便。)断言是单元测试自动化的核心:自动判断运行结果是否符合预先编写好的结果。例子:assertThat(result,is(“expected-output”)),期望 result 这个变量是个字符串格式,值是"expected-output"。如果没通过判断,断言 API 会抛出异常,让这个测试用例失败,并打印出失败的原因。
写 Java 的测试用例时要留意一下 IDEA Auto Import 的哪个库的注解|API,不能无脑回车导入。因为都是用在测试场景,不少库有同名或名称相似注解|API(例:org.junit.Test 和 org.junit.jupiter.api.Test)。注解|API 间需要搭配使用,混用可能使某个注解|API 失效导致测试运行失败,报错也总是云里雾里,无法快速发现是注解用错的原因。这个需要多读点教程和文档弄明白常用的注解|API 间的关系,又是一笔额外的学习成本。
顺带一提,上面的 Hamcrest 基于的测试理念是“基于用例的测试”,即提前写好预测的结果,一个用例只能预测一种行为的一个输入和对应的结果。现在业内出了一个还没流行起来的新测试理念——“基于特性的测试”,简单说就是测试框架根据给定的范围,自动生成测试数据,一个测试用例可以用大量的随机数据测试被测组件的抽象行为。但是这种测试用例特别难写,主要是难以抽象总结被测组件的行为。感兴趣的话,可以阅读这篇文章中的后两节。
IDEA 的 debug 功能挺强大的,尤其是 debug 中途修改变量值,可以作为写不出单元测试时的替代操作。它还允许现改代码并编译替换运行中的类文件,允许 drop 堆栈来实现“反悔”回到上一步调用处,使得程序可以在一段逻辑中反复运行,测试各种情况。可以用上文提到的 Learn 插件学习 IDEA 的 debug 功能,或者读官方文档。
#编程思想
#编程原则
编程原则(principle)是一些抽象化的格言,用来规范提醒程序员的编程行为实践。比如提到面向对象编程实践就是SOLID 五大原则。还有什么 ETC(easy to change)原则,DRY(do not repeat yourself)原则,FEP(fail early,不够仨单词的原则会把原则的P也算进缩写)等,这里有一篇解释了部分常用编程原则的文章。这些原则不是术语,算是程序员间的共识(或者叫黑话),而且可能同一原则有不同解释方式和缩写变体。
编程原则试图从抽象层面对抗未来必然会出现的代码变更,尽可能让代码变更变得容易,少修改已经写好的代码。我借用一下忘了哪本书中提到的观点:把软件开发学科与建筑学科相类比的观点(软件架构 architecture 一词是从建筑学借来的,软件设计模式也是基于建筑学典著建筑模式语言启发出现)已经过时,因为开发流程不再是严格遵守计划表的瀑布式开发。软件开发更像园艺,你把种子种在花园里,不能控制植物的自然生长(未来的需求变更),只能通过园艺技术(代码重构)修剪、梳理花园。
编程原则无关编程语言,甚至在编程之外的工作和生活中都能借鉴,算是一种行为哲学。希望你能聆听这些编程原则,写出易于变更的代码。
#设计模式
请看这篇文章了解设计模式:圣杯与银弹 · 没用的设计模式。以我的经验而言,设计模式最大的用途是减少沟通成本,冲这个用途值得花时间学一些常用的设计模式的概念。设计模式在不同编程语言上的实现会因为编程语言的特性而不同,所以你也会看到诸如“Xxx 语言的 Yyy 设计模式”的标题。
#完成编程工作所需的外围技能
你可以在网络上搜索什么"Xxx 职业技能树|知识图谱|技术栈",有许多人试图总结出这么一个东西,但是要我说,看看这位有10年间有多岗位经验的 reddit 老哥是怎么描述这个行业的技术栈的:
技术栈不重要。技术领域有大约 10-20 条核心原则,重要的是这些原则,技术栈只是落实它们的方法。你如果不熟悉某个技术栈,不需要过度担心。
#英语
四级,英语四级水平的英语读写绝对够用了,只要日常工作没有听说英语的需求。除开每次写方法名都要愣一下单词怎么拼(记得在 IDE 上安装拼写纠正插件,别闹了笑话)以外,编程工作不是太要求英语水平。编程相关的问答、教程、官网、文档…都不会用很生僻的单词,大家都倾向于用最简单的单词表达意图。唯二的例外是博客和论文。博客是口语化的文章,我碰到过不少次作者想举个例子或者玩笑幽默一下,但把我卡住用字典查里面的生词,继续读下去明白了只是一个例子|玩笑的无奈情况。论文…论文的生词主要是术语,数学术语、机器学习术语、数据分析术语等。
有些中文程序员不喜欢英语材料,可能是因为不习惯或者读得慢浪费时间。其实我读得也不快,有时候也因为怕浪费时间选择找中文材料去读。但是如果你去读,你会发现英语材料是可以读懂的,读懂就足够了,多读一些就能克服不习惯的心理障碍,也能慢慢提高阅读速度。
你还可以借助科技的辅助:DeepL Translate等翻译网站、浏览器翻译插件和 IDE 翻译插件等可以帮你弥补英语能力的不足。
#版本控制工具
源代码是文本文件,对吧。版本控制工具 是用来管理文本文件的内容变更的功能,类似 Microsoft Office Word 里的 Review(审计?)功能,就是写论文时导师用来帮你打批注、改论文的那个。我见过有写手用它管理 txt 格式的小说文件,写代码真的和写作很像啊。版本控制工具不仅会记录哪个人对哪个文件做了什么改动,还允许你独立地管理这些改动,确保多人协作的同时维护源代码文件不会被搞成乱糟糟的“源码-v02的副本(1)(2)(1)”这副模样。
有两款主流的版本控制工具工具:Git和SVN(曾经还有CVS,SVN 成功取代了 CVS)。两者区别?打开 Git 的链接阅读介绍。另外我想分享一个写得不错的英文的 Git 教程系列,它不仅讲命令,还总结命令间的区别,还稍微讲了一些 git 的实现原理,让你对 git 命令的执行过程有更深的认识。
#Linux 命令行操作
现在有三种主流的个人计算机操作系统,微软的 Windows,苹果的 macOS,和开源的(各种发行版的)Linux。服务器操作系统就很多了,五花八门的商业产品,比如 Windows 的Windows Server,还有各种 Linux。Linux 系列操作系统因为其内存占用小、运行稳定、开源(即,免费,服务器版本的操作系统超贵)等特点,成为了主流的应用服务器的宿主操作系统。就是说,我们编写的应用在编译打包后,最终要部署到运行着 Linux 操作系统的服务器上。
如果你在 Windows 或 MacOS 操作系统上编程,一般你会使用 Linux 服务器做这两种工作:
部署(也称发版,发布新版本)程序。有时会有专门的运维职业的人员负责这方面工作,或者有自动化构建流水线,有时现实没那么美好,要程序员自己干。部署的操作流程视编程语言而定,在中途加入的项目组中,一般会有其他程序员写好的部署脚本(Shell 脚本)来简化部署操作。
查看日志文件。当程序打包部署后,我们就不能以 debug 的形式查看程序内部的运行情况了。因此我们会在程序中各处插入打印日志的语句,日志会被打印到一些文本文件中,我们通过查看日志文件来判断程序是否正常运行。看日志文件基本是程序员的职责,比如当自己负责的功能出问题时…就会有电话联系你“看一下某请求的情况”,你就要去对应的服务器上查看日志,来判断到底是你的功能出 bug 了,还是其他系统或功能形成的异常数据致使你的功能运行不正常(这种情况也要增加异常处理补丁去防止再次发生,编程原则:永远不要信任外部系统)。如果只是看日志文件的话,最低限度需学习的命令有切换目录的 cd,查看目录内容的 ls,使用正则过滤查看日志文件的 grep。当然你还需要咨询同组其他人、或阅读程序中的日志输出配置文件来了解日志文件的位置。
如果你不熟悉命令行界面,看这篇英文介绍或者菜鸟教程的中文 Linux 教程。另外,在命令行中输入“man”(代表 manual)+其他命令,可以打开命令的说明书;或输入命令+-h
或--help
参数,打印命令的简单说明。你也可以在网上搜索在 Linux 中应该使用哪个命令做你想做的事。
特别的,从我们自己的电脑连接到服务器来使用服务器的命令行界面,需要使用SSH这么一种命令行工具(同时它也是工具原理的同名协议)。一般不需要直接使用它,有多种图形界面软件供你选择,这里不再列举,请自行搜索。关于服务器的 IP、端口、用户名与密码等连接所需的信息,你需要咨询同组的其他人。
#对各种外部系统的配置
提问!上文中出现了几次“配置”?(提示:使用搜索快捷键)开个玩笑,其实我是想说,编程离不开对外部系统的配置。编程项目不是从零开始的,对吧,我们要使用许多已有的系统来省事,比如数据库、中间件、框架、日志系统(打日志是一件很复杂的事)…这些系统都需要我们通过配置来调整它们的行为,以适应我们项目的需要。
一般我们说配置一个系统,指的是编写配置文件,配置文件是一种遵守某种标记语言(xml,json,toml,yaml…)的文本文件,它就是系统的设置项,和手机设置一样。系统一般在启动时读取配置文件,基于此调整行为。注意,系统一般只会在启动时读取配置文件,所以如果系统启动后你想修改配置文件并让它生效,往往需要重启系统。当然这有很多例外…比如你可以多开一个守护进程专门监控配置文件的变化,若出现变化,就重启系统让系统读新的配置文件。另外在我看来,配置文件的内容是只能被该系统识别并解析的“语言”(领域特定语言)。
为了写出配置文件,要去读说明书。可以读官方的配置说明文档(往往详细又冗长),也可以省事读简化版的第三方文档,比如中文翻译、博客记录等。这个工作最麻烦的步骤就是要硬着头皮读大篇幅的文档。读哪个都行,能明白怎么写完成工作就好,我推荐配合网页内搜索读官方文档,毕竟最详细。注意配置与软件一样都是有版本的,要读自己项目所用的版本的对应说明书,或者有些文档只有一版,但会在每个配置项下标注从哪个版本开始生效。
#总结:太多了,先学哪个?
不好意思,我没收住字数,写太多了。咚地一下全摆在面前是挺吓人的,而且说实话本文仍未涵盖我所认定的所有“必备技能”,如正则表达式等。
总之先学编程语言吧,然后是熟悉框架和几个常用的库的用法,此时就能完成工作了。然后如果你对这个职业仍有兴趣,可以学习单元测试、debug 和编程思想(看书看博客),还有外围的技能,这些可以慢慢来。额外的知识会提高你的专业性,让你能够有底气地承认自己被付钱来编写程序,你对自己写出的程序有信心且对它负责。
如果你对这个职业不感兴趣,学到足够工作使用的知识就可以停止了,你也可能会发现其他兴趣和职业的知识会迁移性地对编程工作产生意想不到的帮助,很多抽象的原则和知识是共通的。
#附录
#有用的网站
下面是通用的几个网站:
-
StackOverflow 它是StackExchange 问答网站中分离出的编程问答板块。如果想解决程序报错、想知道某个 API 怎么用、不知道如何完成一个功能、想了解一种编程实践的理念…一切关于编程的东西,基本上你不是第一个碰到这个问题的人,stackoverflow 上大概率会有对应的提问和专业亲切的回答。为了不限制搜索引擎的能力,不建议使用网站内部的搜索功能,而是直接在搜索引擎的搜索栏上开头写"stackoverflow" + 你的问题描述(用英文)。关于这个网站有不少 meme 赞美它的便利,比如:在没有 stackoverflow 之前程序员怎么可能顺利写完程序呢。PS: stackoverflow 需要从
ajax.googleapis.com
域名加载 Js 脚本,因为 GFW 封禁了该域名,在国内网络打开该网站的速度会慢得不可忍受。如果你没有网络代理,你可以使用浏览器的广告屏蔽插件规则将该脚本的加载屏蔽,进而恢复正常的网页打开速度。 -
MDN(Mozilla Developer Network) Web docs 这是实质上的互联网唯一的 Web 前端开发参考文档,附带翔实有趣的教程(多语言翻译,有中文)。你可以不走出这个网站而学习完前端开发所需要的大部分基础知识。
-
Github 结合这篇文章从 Github 里找点感兴趣的项目看一下,感兴趣优先,真的什么千奇百怪的项目都能搜出来。另外有一个小诀窍,如果你拿不准某个库的某个 API 怎么用,而注释、文档和 stackoverflow 又没能让你满意,你可以在 github 中搜索使用了该 api 的代码片段,学习使用方法。
-
Reddit-ExperiencedDevs 这版块只允许3年以上从业经验的开发者发帖,版块里聊的都是职业规划、公司中发生的事等等,很少涉及技术。看这里面的帖子就像一群大佬围着聊闲天时你凑进去听,只是听就能学到不少东西。
下面是 Java 方面的网站:
-
Baeldung 这网站上有内容充实的关于 Java 与 Java 生态(如 Spring 框架)的教程,质量比那种内容农场网站高出一截。不需要刻意在网站中搜索,当你搜索 Java 相关教程时,这个网站往往排在搜索结果前面…
-
Oracle Java Manazine 因为 Java 不太受同行待见(这是真的吗?),所以不怎么能看到关于 Java 语言的公开讨论。虽然(仅在中国?)工作上用 Java 的程序员很多,但讨论 Java 语言俨然是个小圈子。这时我们要借助学术界中小圈子交流的方法:研讨会和期刊。研讨会…至少我不够格参与,因此我选择邮件订阅 Oracle 公司组织编撰的免费杂志,进而一窥 Java 行业的动向,学点新东西。
#之前没接触过 IT 行业的基础知识?
基础知识体系无法突击补习,这是慢活,更别提还会遗忘。目前大学的软件工程|计算机科学专业会开设大约10门左右的 IT 专业课程,也就是说你和科班出身的 IT 专业本科毕业生的差距在10本左右的教科书,以及几个练手的小项目。
不补基础知识体系的前提下完成公司要求的编程工作是可以的,现在的编程语言和工具已经把底层实现都封装起来了。但它会在工作上限制你的思考:不知道原理,就没法想象你的程序如何运行,也就没法深入地思考一个解决方案的细节,心里很不舒服。或者…小心货物崇拜编程,它是指新手程序员常犯的那种“网上找解决方案复制粘贴下来”的不良编程实践。越不熟悉基础知识,越可能在工作中出现货物崇拜编程现象,这可能会在程序里埋下炸弹,最终伤害你或身边的程序员。
这里有一些好的起步课程:
-
【中英字幕|2019 CS50|哈佛大学编程入门】 CS50 Harvard University2019 用非术语讲解 IT 行业里的一些知识体系。真正的0基础开始学。可惜课不太全。【哈佛大学-编程入门】 CS50 Harvard University(2016)11集(全) 这个课全了,可惜没中文字幕。
-
A Guide to Declaring Computer Science at Berkeley UC 伯克利大学的 EECS(我理解为国内的软件工程)的教学资源在它的官网上都有公开分享,包括 Youtube 上的讲课录屏、教义、实验等。起步课程是 CS61A(适合没有编程经验的学生上手编程,且为学生灌输来自SICP神书的关于计算机程序的一系列抽象概念(概念成了体系,日后可以帮你事半功倍地理解更复杂的模型)。
#我推荐的阅读材料
API 的使用方法可以通过关键字去搜索文档和问答,但职业经验就不方便搜索了,它们恰恰是重要的东西。IT 行业很有意思,我没见过除此之外哪个行业在遵守工匠道路的同时,又不吝啬甚至乐于向同行分享自己的经验。有许多人会写经验类的博客,比热门博客更系统化也更著名的是出版成书的著作。下面是我读过的,能打包票的经验方面的阅读材料。
- Clean Code 代码整洁之道 讲细小层面(语句级别)的编程技巧。当用某种编程语言写了一个练手项目或几个功能时就可以开始看了。
- The Clean Coder 程序员职业素养 很薄的一本书,对的,和 Clean Code 是同一系列, 该系列还有两本:架构整洁之道,敏捷开发实践,等级比较高,我还没看¯\_(ツ)_/¯。不涉及技术细节,指导程序员在某件工作乃至职业规划上应该如何做。
- The Pragmatic Programmer: From Journeyman to Master 程序员修炼之道(第2版) 分了许多小章节,每个章节用一个比喻做标题。两个作者想把他们的职业经验讲给你听,内容涉及方方面面。
- 实现模式 书不厚,类似 Clean Code,讲细节的编程技巧,但是对读者的经验要求比 Clean Code 高一些。
- 如何成为黑客 不,hacker 不是你想象的那个意思,你指的是 cracker。这篇文章是一位温和派的开源运动领导者写的关于实践开源精神的一些建议,里面一些原则值得以程序员的角度去理解学习。
- 计算机简史(第三版) 学校里没有开设关于 IT 行业的历史课,我倒觉得该开一门。了解历史能多明白一些为什么现在的 IT 行业是这副样子。比如,开源运动如何让行业内出现了可自由使用的第三方库,让软件研发行业对人员专业素质的要求大幅降低,进而让提供了大量岗位。随着科技的发展和商业需求、社会环境的演变,曾经的一些黄金实践原则已经过时或不合时宜,也出现了适应时代的新的原则。举个例子,你不会在50年前要求程序员编写单元测试,因为他们连编译个程序都要等好几个小时,而现在的算力如此廉价,在一台个人笔记本上运行整个项目的整套单元测试只需要几分钟,这也让持续集成成为可能。
- 人件、人月神话和大教堂与集市 这三本是软件开发组织架构方面的理论书,结合对应的年代和历史事件,它们会让你了解软件开发行业是怎样从瀑布式开发转向敏捷开发的,开发团队的组织规模为何从大部队分散成灵活的小组。
- A Philosophy of Software Design 编程的暗线就是管理(代码之于人的)认知负载,最好的增量设计是最小程度提高认知负载。
- Code That Fits in Your Head : Heuristics for Software Engineering 一本好书,类似《程序员修炼之道》,从零开始写一个项目的一部分,然后讲每个阶段可以遵循的好实践。我对这本书最大的体会是:编程是掺杂艺术和工程的行业,工程部分的方法论可以从其他人那学,艺术部分的只能通过自己实践积累经验。