对于PHP中static与非static方法使用场景的疑惑🤔

问题:
  • 静态方法和普通对象成员方法各有什么优缺点
  • 在业务场景中使用哪种更优?

如:getUser为静态方法:
UserModel::getUser();
或:getUser为普通对象成员方法:
$userModel = new UserModel();
$userModel->getUser();

描述

场景是这样的,在做项目的时候我看到搭档(非同事)写的代码都是static的,但是我们公司业务中又很少使用static,看到这么多static我就很不适应,但是呢,static确实能增加我们编码的效率,很纠结,然后就想去探究一下在我们的项目中,static有什么优缺点?

我在想到问题的时候脑袋中有几条想法:

  1. static的生命周期是一个请求的起始,整个请求对于实例只需要实例化一次,不必每次需要使用方法/对象时都创建实例。
  2. static是在编译的时候赋值,动态类是在程序运行的时候动态分配的。
  3. 如果只调用一次的情况下,static在编译的时候多做了些工作,而非static是在执行的时候多做了一些工作,差别不是很大。但是在多次调用的情况下,静态类是编译的时候就赋值了,程序之后的运行都可以直接调用过来,而不用动态的分配内存了,就节约了时间。
  4. static是全局的,可能会存在一处改动从而引起别的地方也出现改动。即我有一个操作数A,B在操作A的时候可能会污染C去操作A。
  5. static会维护一块内存在请求的起始,假使我们一个请求中的逻辑相当复杂,我们定义了很多的static,这时候会发生什么?

很明显,上边几个想法有了一些冲突,很纠结,赶紧查一下吧,于是我找到了一个还算靠谱的文章(OO PHP static keyword, should I use it?。原来这个问题不是我一个人有。这边文章有以下一个总结:


简而言之:不能。静态方法通常被认为是不好的做法。他们引入了很多潜在的问题。它们是将程序代码引入OOP的一种方式。

  1. 他们引入了隐藏的依赖关系。任意调用foo::bar()的代码具有对foo的依赖关系,并且在未定义foo的情况下无法运行。在这种情况下,使用foo::bar()的对象将正确构造,但如果未定义foo则将无法使用。它只会在调用foo::bar()时中断。如果将$foo实例传递到对象的构造函数中,则没有它的依赖关系甚至无法构造该对象。这将创建更加健壮的代码。
  2. 静态变量是全局变量。全局状态非常糟糕,任何可以更改代码的状态都是未知的。使用静态方法会牺牲OOP封装所实现的功能和控制。
  3. 用不同的版本替代功能是不可能的。在foo::bar()示例中,您牺牲了使用静态方法在OOP中启用您的所有功能多态性。
  4. 这使得不可能进行单元测试。对于单元测试,您需要能够将依赖项替换为模拟。使用静态方法使这一过程变得更加困难。
  5. 您确定只需要一个实例吗?想象一下,如果您有一个创建FTP连接并允许使用简单API创建文件的类。只需调用FTP::upload($file);会很干净不是吗?问题是,一旦要求更改为“现在是否也可以将文件上传到备份服务器”。那么您就有问题了:无法同时连接到两个不同的服务器。

当然,应避免使用静态方法。从OOP的角度来看,没有理由应该使用方法在对象上选择它们。


按照上边这种说法,在使用static的时候是不安全的,可能会出现一些非常糟糕的问题。

如果我们假设上文的总结是对的,它印证了网上很多文章说static多用于工具类中的说法。但是对于业务中我们究竟能不能使用static以及使用与不使用的优缺点在哪里还是有些疑惑?因为我在使用static的项目中并没有遇到这些问题。

所以在这里请教各位大佬,谢谢各位🙏

阅读 5.4k
3 个回答

在大多数情况下,PHP是通过 FPM 运行,如果你换到非 FPM 环境,你就会发现出现一些奇怪的问题。

从项目讲

  • UserModel::getUser(); 像这样的代码,看似是用静态的方式调用了非静态方法,但是往往在其实现都是使用的 __callStatic 方法,然后 new 一个当前实例在调用,也就是说多了一层调用。
  • 还有就是上面你提到了,不可测试性、(相较于有容器管理的实现中,还具有不可替换性)。

当然,如果有一个情况,比如要对数组去空、去重、排序、这种 一连串会复用的方法组合 去创建一个 Utils 类并创建一个 静态方法是比较好的一个实践,比如 Laravel 中的 Str 、和 Arr。

对于一个对象,应该尽量避免使用、创建静态方法。

上面你引用的文章也讲得不错。

UserModel 对应user表如果new 这个模型就算一条记录,那么$userModel->getUser();是一条记录去获取另一条记录?说不过去,而UserModel::getUser();就是user这个表里面去获取user用户

静态方法调用速度快,需要有状态存储(类属性读写)的,必须得实例化后调用

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题