公共知识杂谈

编程语言

编程语言的学习是我们进入软件世界的基础阶梯,著名的 Code Complete 一书中提到:Program into Your Language, Not in it. 我们不应该将自己的编程思维局限于掌握的语言提供的那些特性或者概念,而是能够理解这些语法特性背后能提供的抽象功能与原理,从而能够根据自己想要达到的目标选择最合适的编程语言。而从另一个角度来看,无论哪一门编程语言的学习也是具有极大的共性,从严谨而又被诟病过度冗余的 Java 到需要用游标卡尺的 Python,从挣扎着一路向前的 JavaScript 到含着金汤匙出生的 Swift、Rust,我们都能够发现其中的相通与互相借鉴之处。

语法基础

任何一门编程语言的学习都需要从基本的表达式(Expression)语法开始学习,我们需要了解如何去声明与使用变量、如何为这些变量赋值、如何使用运算符进行简单的变量操作等等。在很多语言之中都有所谓的传值还是传引用的思量,譬如 Java 与 JavaScript 本质上就是 Pass-by-Value 的语言,只不过会将复杂对象的引用值传递给目标变量。这个特性又引发了所谓浅复制与深复制、如何进行复合类型深拷贝等等需要注意的技术点。除此之外,作用域与闭包也是很多语言学习中重点讨论的内容,在 JavaScript 与 Python 的学习中我们就会经常讨论如何利用闭包来保存外部变量,或者在循环中避免闭包带来的意外变量值。表达式是一门编程语言语法基础的重要组成部分,接下来我们就需要去学习流程控制与异常处理、函数定义与调用、类与对象、输入输出流、模块等内容。流程控制的典型代表就是分支选择与循环,譬如不同的语言都为我们提供了基础 for 循环或者更方便地 for-in 循环,而在 JavaScript 中我们还可以使用 forEach 与 for-of 循环,Java 8 之后我们也可以基于 Stream API 中的 forEach 编写声明式地循环执行体,而 Python 中的列表推导也可以看做便捷的循环实现方式。异常处理也是各个编程语言的重要组成部分,合理的异常处理有助于增强应用的鲁棒性;不过很多时候会出现滥用异常的情况,我们只是一层一层地抛出而并未真正地去处理或者利用这些异常。Java 中将异常分为了受控异常与不受控异常这两类,虽然 JavaScript 等语言中并未在数据类型中有所区分,但是却可以引入这种分类方式来进行不同的异常处理;有时候 Let it Crash 也是不错的设计模式。

Eric Elliott 曾在博文中提及,软件开发实际就是 Function Composition 与 DataStructure Design;函数或者方法是软件系统的重要基石与组成。我们需要了解如何去定义函数,包括匿名函数以及 Lambda 表达式等;尽管 Java 中的 Lambda 表达式是对于 FunctionalInterface 的实现,但是鉴于其表现形式我们也可以将其划归到函数这个知识类别中。接下来我们需要了解如何定义与传入函数参数,在 C 这样的语言中我们会去关心指针传递的不同姿势;而在 JavaScript 中我们常常会关心如何设置默认参数,无论是使用对象解构还是可选参数,都各有利弊。Objective-C 与 Swift 中提供的外部参数就是不错的函数自描述,Java 或 Python 中提供的不定参数也能够帮我们更灵活地定义参数,在 JavaScript 中我们则可以通过扩展操作符实现类似的效果。然后我们就需要去考虑如何调用函数,最典型就是就是 JavaScript 中函数调用的四种方式,我们还需要去关心调用时函数内部的 this 指针指向。而装饰器或者注解能帮我们更好地组织代码,以类似于高阶函数的方式如洋葱圈般一层一层地剥离与抽象业务逻辑。最后在函数这部分我们还需要关心下迭代器与生成器,它们是不错的异步实现模式或者流数据构建工具。

近几年随着前端富客户端应用的迅猛发展与服务端并发编程的深入应用,函数式编程以及 Haskell 这样的函数式编程语言也是引领风骚。尽管面向对象编程也有着很多其他被人诟病的地方,但是在大型复杂业务逻辑的应用开发中我们还是会倾向使用面向对象编程的范式;这就要求我们对于类与对象的基本语法有所掌握。我们首先要去了解如何定义类,定义类的属性、方法以及使用访问修饰符等方式进行访问控制。其次我们需要了解如何从类中实例化出对象,如何在具体的语言中实践单例模式等。然后我们就需要去了解面向对象的继承与多态的特性,应该如何实现类继承,子类与父类在静态属性、静态方法、类属性、构造函数上的调用顺序是怎样的;以及如何利用纯虚函数、抽象类、接口、协议这些不同的关键字在具体语言中实现多态与约定。最后我们还需要去关注下语言是否支持内部类,譬如 Java 就分为了静态内部类、成员内部类、局部内部类与匿名内部类这四种不同的分类。在整个语法基础部分的最后,我们还需要去了解下输入输出流与模块化相关的知识,譬如 Java 9 中即将推出 JPMS 模块化系统,而 JavaScript 的模块化标准则历经了 CommonJS、AMD、UMD、ES6 Modules 等多轮变迁。

数据结构与功能

语法基础是我们掌握某门编程语言的敲门砖,而学习内建的数据结构与功能语法则是能够用该语言进行实际应用开发的重要前提。在数据结构的学习中我们首先要对该语言内建的数据类型有所概览,我们要了解如何进行常见的类型与值判断以及类型间转换;譬如如何进行引用与值的等价性判断、如何进行动态类型检查、如何对复合对象的常用属性进行判断等等。很多编程语言中会将数据类型划分为原始类型(Primitive)与复合类型(Composite),不过这里为了保证通用性还是将学习复杂度较低的数据类型划归到基本类型中。常见的基本类型囊括了数值类型、空类型、布尔类型、可选类型(Optional)以及枚举类型(Enum)等等。学习数值类型的时候我们还需要了解如何进行随机数生成、如何进行常见的科学计算,这也是基础的数值理论算法的重要组成。JavaScript 中提供了 undefined 与 null 两个关键字,二者都可以认为是空类型不过又有所区别;而可选类型则能够帮我们更好地处理可能为空地对象,避免很多的运行时错误。接下来我们就要将目光投注于字符串类型上,我们需要了解如何创建、删除、复制、替换某个字符串或者其他内容;很多语言也提供了模板字符串或者格式化字符串的方式来创建新字符串。我们还需要知道如何对字符串进行索引遍历,如何对字符串进行常见的类型编码以及如何实践模式匹配。模式匹配中最直接的方式就是使用正则表达式,这也是我们应用开发中经常会使用到的技术点。除此之外我们还需要关注字符串校验、以及如何进行高效模糊搜索等等内容;我们也可以学习使用 KMP、Sunday 等常见的模式匹配算法来处理搜索问题。

然后我们需要学习常见的时间与日期处理方式,了解如何时间戳、时区、RFC2822、ISO8601 这些基础的时间与日期相关的概念,了解如何从时间戳或者时间字符串中解析出当前编程语言支持的时间与日期对象。我们还需要了解时区转换、时间比较以及如何格式化地展示时间等内容,有时候我们还需要利用日历等对象进行事件的增减以及偏移计算。接下来就是非常重要的集合类型,无论哪种编程语言都会提供类似于 Array、List、Set、Dictionary、Map 等相关的数据结构实现,而我们也就需要去了解这些常见集合类型中的增删复替以及索引遍历这些基础操作以及每个集合的特点;譬如对于序列类型我们要能熟练使用 map、reduce、filter、sort 这些常见的变换进行序列变换与生成。进阶而言的话我们可以多了解下这些数据结构的底层算法实现,譬如 Java 8 中对于 HashMap 的链表/红黑树实现,或者 V8 中是如何利用 Hidden Class 进行快速索引的。接下来的话我们可以对于像 Java 中 SteamAPI 或者各种语言的 Immutable 对象的实现方式有所了解,还有就是常见的 JSON、XML、CSV 这些类型的序列化与反序列化操作库也是实际开发中经常用到的。

接下来我们就需要对语言提供的常用外部功能相关的 API 或者语法有所掌握,主要也是分为存储、网络与系统进程这三个部分。在存储部分我们需要掌握如何与 MySQL、Redis、Mongodb 等关系型或者非关系型数据库进行数据交互,掌握如何对文件系统进行如文件寻址、文件监控等操作,并且还需要能够使用一些譬如 Java 堆外存储这样的应用内缓存来存放数据。而网络部分我们应该掌握如何利用 HTTP 客户端进行网络交互、如何使用相对底层的 Socket 套接字建立 TCP 连接、或者使用语言内置的一些远程调用框架与远端服务进行交互。最后我们需要对如何利用语言进行系统进程操作有所了解,本部分笔者认为最重要的当属并发编程相关知识。在而今服务器性能不断提升、处理的数据量越来越多的情况下,我们不可避免地需要使用并发操作来提高应用吞吐量。并发编程领域我们应该去学习如何使用线程、线程池或者协程来实现并发,如何利用锁、事务等方式进行并发控制并保持数据一致性,如何使用回调、Promise、Generator、Async/Await 等异步编程模式。除此之外,我们还需要对切面编程、系统调用以及本地跨语言调用有所了解。

工程实践与进阶

编程语言初学阶段的最后我们需要了解下工程实践以及一些偏原理与底层实现的进阶内容。首先开发者应当对具体编程语言中如何实现 S.O.L.I.D 编程原则与数十种设计模式有所了解,当然也不能邯郸学步只求形似,而是能够根据业务功能需求灵活地选择适用的范式。而在团队开发中我们往往还需要统一团队内的样式指南,包括代码风格约定中常见的命名约定、文档与注释约定、项目与模块的目录架构以及语法检查规范等。接下来我们还需要对语言或者常用开发工具的调试方式有所了解,掌握基本的单步调试等技巧,并且能够为代码编写合适的单元测试用例。工程实践方面的最后则是要求我们对代码性能优化所有了解,尽量避免反模式。进阶内容的话则相对更加地抽象或者需要花费更多的精力去学习,其中包括泛型编程、元编程、函数式编程、响应式编程、内存管理、数据结构与算法等几个部分。泛型编程与元编程中的反射、代码生成、依赖注入等还是属于语言本身提供的语法特性之一,而函数式编程与响应式编程则偏向于实际应用开发中有所偏爱的开发范式。即使 Java 这样纯粹的面向对象的语言,当我们借鉴纯函数、不可变对象、高阶函数、Monad 等函数式编程中常见的名词时,也能为代码优化开辟新的思路。响应式编程是非常不错的异步编程范式,这里我们还需要注意下并发编程与异步编程之间的差异。而内存管理则有助于我们理解编程语言运行地底层机制,譬如对于 JVM 或者 V8 的内存结构、内存分配、垃圾回收机制有所了解的话能够反过来有助于我们编写高性能地应用程序,并且对于线上应用错误的调试也能更加得心应手。)

上一页
下一页