我只是想要一个函数啊
从面向对象说起
显然,面向对象编程在很长一段时间内,都是最主流的一种编程范式。那到底什么是面向对象呢?为什么我们需要对象呢?
面向对象的最重要特性有三个:封装,继承,多态。
- 在我看来,多态无疑是这三者中最为重要的特性,它解决了软件架构中最为复杂的依赖问题,也就是我们常说的解耦。
- 继承则解决了代码复用的问题,并实现了一定程度的抽象。
- 而封装则使数据变得更安全,同时也隐藏了数据的存储结构与类型。
对象的本质
假设我们现在要写一个人员管理的软件,需要实现一个给人员年龄加 1 的功能。非面向对象的写法可能是
addOneYearAge(person)
我们拿到一个数据的引用,然后给其年龄加一。而面向对象的方式则可能是
person.growUpOneYear()
这里我们有一个对象,然后给对象发送了一个消息,通知它发生一些变化。
这两种写法都能实现我们的目标,那么那种更好呢?如果我们抛开运行性能,抛开运行时函数入口跳转等等奇怪的字眼,单纯的看看上面两段代码,我觉得他们是一样的(我相信肯定有人会说第二种更好,或许也有人会支持第一种)。但是,当你的数据有二十种改变的方式的时候,第一种写法会崩溃的更快,各种改变 person 的函数会被写在你永远想象不到的地方,而第二种写法则相对容易管理,因为你控制了数据的改变(如果你知道你在做什么的话)。
所以,对象是什么已经很显而易见了。对象是一堆数据的集合,以及可以操作这些数据的方法。
闭包
为什么要从面向对象扯到闭包?这两个东西八竿子打不着啊?
从前我也是这么认为的,直到有一天我读了 ruby 作者松本行宏的书《代码的本质》。他说:对象和闭包是同一事物的两面。下面是一段简单的 js 中使用了闭包的代码。
function outside() {
var a = 0;
return function inside() {
a++;
console.log(a);
};
}
var f = outside();
f(); //1
f(); //2
f(); //3
我们可以看到 outside 函数返回了一个 inside 函数,inside 函数可以访问 outside 函数作用域内声明的变量 a。
通常情况下,我们认为当函数返回时,其调用栈的栈帧(stack frame)以及其上的变量就会被销毁,所以当 outside 返回的时候,a 就应该不能访问了,但事实是 inside 函数可以始终访问到 a 变量。关于闭包可能导致的内存泄露问题不是本文讨论的重点。真正让我觉得有趣的依然是松本先生的那句话:对象和闭包是同一事物的两面。
其实,我们可以认为对象是通过类机制将过程(方法)封装到了数据里面,而闭包是通过作用域将数据封装到过程(方法)里面。它们的本质都是将数据与操作数据的过程(方法)放到一起,将数据与计算放在一起罢了。
(如果你还是不能理解这段内容,请仔细阅读《sicp》的第二章。)
日常黑 spring
下面开启吐槽时间,热爱 java 或者 spring 的同学请迅速撤离,或者准备好和我”撕逼”。
最近公司的后端技术栈正在尝试从 nodejs 转到 spring(主要是为了好招人吧)。可能是写的还不够多,反正以前那种写代码 smooth 的感觉一下消失了。当我需要一个函数的时候,我需要写一个接口,再写一个类,再把它装配进来。为什么我不能直接使用这个函数呢!!!为什么我要写那么多没有任何数据存在的类!!! 肯定有人要说我 too yong,让我去了解一下 oop 再说,或者找 uncle bob 的视频看看。但我真的真的只想要一个写了一堆函数还有一些常量的包,而不是去 autowire 一个单例的对象。
肯定也会有很多人说这不是 spring 和 java 的锅,在 jvm 中,当你需要一个函数时,你只能找到一个对象,然后找到这个对象的类,然后去 method area 找到这个类的各种方法,然后找到你的函数入口。。。真的很蠢啊。而且,难道你还想让运行时来给你背锅吗?作为一名普通程序员,我管你什么运行时编译时,什么内存布局,我只是想要一个函数啊。
如果只考虑代码,spring 和 java 真的不是 the right way to do things。但是,现实又怎么会只考虑代码呢?