昨日在博客发表了一篇文章(点击进入查看),网友@ichenshy 提出了一片质疑之声,论及:框架存在的价值,Class和function在PHP运行时的性能问题等,这不会改变我的看法和观点,但是我真心觉得他说得很好,为什么这么说呢?我先把他的观点挪过来,大家看看。
第一次回复
1、弱类型有弱类型的优点,强类型有强类型的好处,在哪些地方非得进行类型判断呢?
2、实在不明白为什么对于PHP这样的解释性语言非得在框架里堆砌一大堆的class,有些事明明一个function就能搞定的;
3、框架是为了规范写代码的人,还是帮助写代码的人自动完成一些重复性的工作?
4、框架弄得太复杂了,给人使用的时候学要多长的学习成本?后续维护的人接手的时候需要多长的时间完全弄懂?
第二次回复
你题目的重点“运行效率”,我说的2点也是说这个!在PHP这种解释性语言中,fn无论是在内存占用还是性能上面都要比cls要好很多,不管在哪个版本中都一样。既然“cls、obj是一种代码管理和组织形式”,那为了“代码管理和组织形式”牺牲“运行效率”值得么?特别是“运行效率”无法在代码方面解决的时候只能再掏真金白银来买硬件。我并不是反对面向对象,只是反对一味的面向对象,明明一个fn就能解决的问题,非得弄个cls,特别是一大堆cls的时候,就丢失了PHP的优势,还不如去用JAVA!“面向接口设计,为软件设计,架构设计提供稳定性和可靠性”——面向过程或者两者混合难道就不能“为软件设计,架构设计提供稳定性和可靠性”?用C写的哪些大型项目不是也有很多吗?“对象实例,比起值类型判断要简洁明快”——内存占用也会多很多,运行效率也会差很多!“参数类型说明,直接避免了不可靠的参数进入”——如果是外部直接提交的参数怎么说倒是可以理解,如果是怕写代码的人犯这样的错,那你要防范的事情就太多了!
首先交代清楚我的立场,我不想在我的博客里面和别人吵了乱七八糟的,因为博客的评论很乱。论坛讨论区的功能非常适合争论,所以楼主按理说应该是 @ichenshy 我只是将他的内容收集,并转移过来,我的回复、我的观点,会在这个贴的回复里面交代,那时我会作为反立场。
同时解释一下为什么说我觉得他说得好!质疑,是说明人在思考,有自己的思想。Class和function之争,PHP里由来已久,包括框架该不该存在等,都是经典之争。我会尝试表达出我的看法与见解,不过我不会立足于争论!
好,现在开始说明我的观点,先简单阐述一下,我这一年里在升级过程中所做过几次大规模的调整。
其中有一个阶段,大概去年9月到11月之间,我就为了到底应该基于function还是基于Class去组织框架核心的代码,进行过大量的测试和性能比较,最终选择的方案是基于Class,其中得出了一些测试的数据。
call function 10000次,设定运行时间为x(同一台机器上)
call Class::function 10000次,其运行时间约为x * (1.05 ~ 1.1),1.05 ~ 1.1表示为一个取值的范围。
call new Class 10000次,运行时间约等于x,小于x * 1.05。
当然,这里三种模式,执行的操作是相同的,这结果也包含了空函数的测试。
不过问题不仅仅在于调用一个函数所消耗的时间成本。使用function或Class模式,是两种迥然不同的风格,其中需要调整最多的,主要是全局环境的变量存放载体,和这些变量的闭合性处理的问题。说得通俗点就是,函数在运行过程中(以一个项目整体而言),产生大量的中间变量,这些中间变量指的是可用于其他函数运算时使用的,这些变量存放在哪里,是使用function或Class后,第二个争论点所在,包括如何调用。
也正是基于这第二个争论点,我最终选择了基于全局静态Class(就是Class::function)的模式,内部使用实例化的模式(new Class)来组织核心代码,重要的函数使用function形式,作为函数库的形式存在。
使用function的话,项目配置,供其他函数使用的中间变量,基本上要存放在$GLOBALS的空间里,事实上,在PHP 5.3以前的版本时,这是最稳妥的做法,因为当时PHP的GC有个bug,如果把Class实例作为类属性存放,需要手动注销,当然5.3以后就解决了这个bug。
题外话:不过PHP 5.2的这个bug,其实蛮严重的,框架2.0的时候,没注意到这个问题(PHP最初也没有公布这个bug),所以在项目面临高并发的情况下,都会发生内存溢出的问题,不得不强迫重启Apache,后来针对这种情况还特别做出调整,将所有的中间Class实例存放到$GLOBALS变量里,以解决了这个问题。
存放在$GLOBALS空间里,最大的好处是调用方便,写入直接,完全不需要考虑通过接口实现,完全是0成本。不过也碰到最大的问题,就是这些变量的闭合性的问题,有些中间变量,是不希望被直接修改的,尤其可能涉及项目运行模式的那部分,还是希望有一个接口来控制。
除了$GLOBALS空间以外,还有就是局部static变量,这东西真是……他和Class的static是一样的实现思路,初始化赋值不允许运算,只能赋静态常量和语法结构。
static弥补了纯粹存放在$GLOBALS空间的问题,的确做到了将中间变量隐藏在这个函数内部可见,不过同时又存在开放性的问题。A函数内部的static是不能和其他函数共享访问的,那意味着,如果你想访问这个static,你的函数必须设计的非常……
当然,还有老传统,就是传引用,这个东西总能在关键时刻,给你很重要的帮助。不过他的缺点也很明显,可控性太低。关于传引用的问题,有很多资料,我就不罗嗦了。
可以得见,当使用function时,闭合性方面是没有很好的解决方案的。就算能勉强实现,支撑起一个局面,可是越到深处,会发现,需要隐蔽起来的变量,会越来越多,同时这些隐蔽的变量,又需要公共接口访问。使用公共接口访问的好处是,得意在接口处,你还可以做一层处理,提供更大的编程可能性。而就算是用函数局部的static,又需要把函数的参数设计的异常诡异。而我个人认为,一个能易于被学习、被使用的函数,他的参数应该是少量,且合理的,而不是多变的,不可预测的——所谓可预测,是一个十分重要的特性,一个函数,调用1、2次,基本上就能推测出起参数传递方式,便于被记住,这样这种函数被使用的几率也最大,如果一个函数一会一种情况,要怎么用,还得看看说明文档,这是很糟糕的函数设计。
使用$GLOBALS实际上还存在另外一个问题,立于PHP这个语言来说,直接读取$GLOBALS应该最快的做法,不过事实上HashKey查找的成本,你没有计算进去。为此前几天,我还为此专门拿node.js和PHP做了一个比较,比较的就是HashTable的性能问题。结论是明显的,node.js的object结构({})性能远远高于PHP的array。其实看他实现出来的东西你就知道了,v8引擎里object是无序的,而PHP的array,即便你把他当hashtable来使用,他本身还是有序的。
相反之下,使用Class的结构去组织代码,全部的变量可以很好的隐藏在类属性中,如果你认为必要,完全可以转用public公布这个属性,如需要,又能很好的将其隐蔽,然后设计接口,提供写入的方法。所以,经过这个层次的对比,Class和function之争,很快有了答案。
当然,这个答案是对我而言,我想,每个人有自己的看法,没来得半分强求吧。我不求辩其长,只求论其明。
按照这个观点,很多解释型的语言完全可以一个function打天下了,要class干啥
框架是框架的问题,class是class的问题,这个需要搞搞区分
class是为了更好的代码组织和复用,而不是为了有个class就用class
全部都是function的一个典型的反面例子就是 ECShop
function 就失去了重载的功能, 这点对于架构会造成严重的影响.
事实上写函数到达一定境界, 是在写类方法. 比如get_cat(); 函数, 取得所有的栏目数组, 我们平时写, 也许就查询一下, 然后return结果即可. 接着又将查询数据库缓存, 接着又会去思考防止函数被调用二次, 函数调用二次不会报错, 却会引发开发者去思考, 这真心是低智商的bug. 所以你又会产生静态数组方式.
楼主, 不要批判class, 可以批判框架.
为此前几天,我还为此专门拿node.js和PHP做了一个比较,比较的就是HashTable的性能问题。结论是明显的,node.js的object结构({})性能远远高于PHP的array。
具体测试结果是多少
引用来自“阿尔法兽”的答案
上层概念和底层的东西不好拿来比较吧。
phper也是码农