我上司给我发一篇 java sonar 啥的文章,他觉得很神奇,我就告诉我上司,2013 年左右 王垠 写了 RubySonar 和 PySonar,我还给上司发了 Rubocop。
他的第一反应是:卧槽这什么鬼玩意(一脸排斥),我跟他说,我的 amber-kit 是借助它辅助编码的。
上司对 Ruby 真的比较反感,他说武汉根本招不到人,是根本上的,他跟我说他朋友的公司前几年就是用 Ruby,后来全用别的语言重构了。
他有一次问我:
为什么你不是用 Go / C++ 来写 amber-kit,先不讨论别的,关键是你这东西没办法招人啊。
由于公司一直用 PHP,其实我对 PHP 真的很反感,不是一般的反感,简直是逆天弑神的反感到炸。
我认为是的,但只是我自己
不需要考虑太多复杂的 Interface、Generic、template 等复杂的东西,用 mixin 就可以解决。
实现的目的,不过就是暴露一个接口,让类与类之间更好的契合在一起,好比古代木匠的卯和榫。
接口的方式是传统面向对象思想的接口(interface)与实现(implement)方式对接两个对象。
模板的方式是通过预编译宏一样的处理,就是模板类 typename T、class T 只要做了 T::method 或者 T t.method 的调用,在模板委托目标类一定要实现。
template <typename T>
void method(T t) {
t.method();
}
class MyClass {
void method() {}
}
MyClass mc;
method(mc); // 调用 mc.method
其实这是一种典型的【代理委托】设计模式,编译器会在【编译时】检查 mc 的类,也就是 MyClass,是否实现了 method 方法,如果你放个 int a 到 method(a) 来调用,会报错 int 没有方法 method。
template 一般是 C++ 这种没有完整元信息的实现语言以预编译形式处理的,也就是说,跟宏接近,模板类不是类型,而是模板
泛型的方式和模板相近,比模板强,区别在于,泛型跟普通类一样,也是类型。
在 Java 编码的时候,你不能 new T,但是你能反射出当前运行时 T 的类再去 new。
说了这么多废话,我要进入正题,开始卖萌了
module Comparable
def >(param)
self.<=>(param) == 1
end
...
end
class Size
include Comparable
attr_accesor :size
def <=>(param)
self.size - param.size
end
end
比起接口、模板、泛型,Ruby 可以更简单、直接的委托一个方法到模块,然后复用。
由于是动态注入的,所以随处都能 include,还可以灵活的 extend,虽然 extend 出来的 single_class_method 的方式有点让人联想 JavaScript 的 bad practice 的 prototype clone style programming。但是 Ruby 并不建议你去主张搞那么多 SCM,而是提供这项功能,发挥其作用。这是真善美的!
动态类型可以解决开发者不用担心编译时太多类型不匹配导致开发者心烦,你试着拿着玉米一粒粒抠掉就能明白你去一个个解决类型匹配问题其中的道理。
Java 抽象做大了,类型不匹配的情况很多,而且超级恶心,很棘手。
Ruby 是强类型的,找不到方法还是会抛出错误,虽然动态会带来错误的可能,但还是比较严谨的。开发者可以先解决问题,后修复 bug,这是自顶向下模型。如果你有强迫症(也不是什么强迫症了啦,只想要严格一点),辣么,每个对象都有 is_a?,提供给你筛选符合的类型并处理不符合的类型。
Python 被很多人公认是很好的入手第一门语言,因为它在强类型动态的特点,让初学者不需要那么老成持重地声明类型、规范类型,但是懂得遵守类型。
这让开发者不需要去必须把规范执行得很死板,但是又能遵守约定类型来开发,不至于像某些情况说“完全不顾类型的直接编写代码”。
PHP 是世界上最“土得掉渣,还告诉爸妈,回家种田吧”级别的语言。
PHP 延续了 Perl 的任务式开发思想,把很多东西都不包到模块里面,直接全局方法调用。
PHP 世界的粒度是以 Scalar 作为基本单位的,每一个 PHP 变量(Scalar)都既是 int 也是 double 也是 string 也是 object,这跟 Perl 5 一毛一样(说人话)。
大家可以用 Devel::Peek 看一下 Perl 每个 Scalar 内存结构就知道了。。。
use Devel::Peek;
$a = 42; $a = "hello";
Dump $a;
# 输出
SV = PVIV(0xbc288) at 0xbe9a8
REFCNT = 1
FLAGS = (POK,pPOK)
IV = 42
PV = 0xb2048 "hello"\0
CUR = 5
LEN = 8
我来跟大家介绍一下,IV 实际上是 IntValue,PV 就是 PointerValue,其实还有很多,像 @array 还会有 RV、ARRAY FILL MAX 等。具体自己看 perldoc
以下引出 PHP 类型结构,跟 Perl 没啥区别
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
unsigned char type; /* active type */
unsigned char is_ref;
short refcount;
};
typedef union _zvalue_value {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} zvalue_value;
Perl PHP 当你第一次赋一个 int 的时候,它在结构体的 int 部分指针 = 被赋值的 int,以此类推。。。
这种复合类型好处是方便不了解编程的人能够方便在 a = 1; a .= "str"; 的时候隐式处理,不需要你 to_s toString() atoi()。
然而,这样已经从设计的最开始就已经违背了类型系统的初衷,应该说,这样的语言的思想是朝着【无类型】设计的,所有的变量、标量都能连在一起运算运行,无需开发者关心。
PHP 天生几乎都是设计成 SAPI,用 C 扩展的,类似于 JSP 之于 Java 的方式,本来就是作为直接网页嵌入编写式语言,跟包管理更不太融洽,如果你来设计维护 JSP,会辣么无聊让本来作为模板语言嵌入的 DSL 增加包管理吗?显然复杂化了。
所以 PHP 天生包管理就不强,include 的东西基本上都不是包,而是文件,然后命名空间真的有点脑残,\Namespace 这种前面要带个斜杠才能用全局的 Namespace,否则的话,你前面设定了自己的 NamespaceA 之后继续写代码会引用的是 NamespaceA\Package。 然后我有个 Laravel fans 说就把 \Namespace 看成是目录,我呵呵哒,你见过 java 会 .java.package 写吗?先天残疾不要把这种问题丢给开发者。
就算有了 namespace,不等于 PHP 就有了包管理,还是有很长的路走的。
PHP 几乎没有 universal standard coding style,而且每个框架几乎都是独立维护的。像 snews 这种程序,是一个老外写的新闻 cms,一个打包好的 php 脚本就是程序全部了,各种奇葩的东西都有,不过早期也是这么设计的。
写过 PHP 扩展的人都知道,有 PHP_MINIT_FUNCTION PHP_RINIT_FUNCTION,尤其 PHP_RINIT_FUNCTION 这个东西,是当一个 REQUEST 请求初始化时运行的,说白了就是 CGI 拿到环境之后开始运行调用的。
这根本就不是一门独立的编程语言,虽然它照样可以使用来进行 low level programming,但是 socket 编程会很有问题,队列操作的时候,如果一个 socket 已经关闭了,那么接下来调用查询状态会出现 FALSE 或者 NULL,但是无论哪种都拿不定,而且 PHP 根本没法判断这个 FD 是个 FD,因为 PHP 对于这种类型根本不是类型,一个比较底层的引用的聚合类型你叫我怎么判断。(这里理解有点难度,如果能读懂我的意思就更好了)
所以才会出现 Swoole 和 Yaf,但是这样的话,PHP 受制约性也太大了,比 Lua 还差,Lua 好歹可以包装对象,虽然不是完整的面向对象,Lua 好歹也有 meta-table 元表,跟 JavaScript prototype 类似的实现。
总之 PHP 就是垃圾中的战斗机,根本不足挂齿,说自己是做 PHP 的都觉得脸红耳赤羞得没法下台了(没事,找个地方羞去,没人看的)……(此处省略一亿字)
这将允许你代码上能快速编码,不需要那么多 number.add().div(),更加灵活
这将允许你进行对象的后天编程,让喜欢抽象的程序员留了一大片自由幻想的天空
无论自己写模块,包装 gem,跟 C 交互,一切都是干净的、卫生的、便利的,因为它的思维粒度是基于 struct class 和 struct object。C++ 的开发者们把它当作 Lua 使用作为你的辅助工具也无可厚非。JRuby 和 Java 也有很棒的交互能力。
约定是 Ruby 最核心的东西,它体现在 Rubyist 手写的每一句表达式、每一个控制语句,以及完成的每一种设计上,它不仅仅让你的编码写作变得相互认可(不强求绝对统一),模块、对象之间交互也变得宽松简约。
另外,这些还是需要一个开发者有良好的编码素养才能达成的,但是也并不难。
看着 Ruby 的开发者越来越受两边排挤,我也看不下去了,希望更多人支持它!