基于PHP静态类的原罪详解_php实例

黑格尔有句名言:存在即合理。以此为论据的话,静态类的使用必然有其合理性。不过物极必反,一旦代码过于依赖静态类,其劣化的结局则不可避免。这就好比罂粟作为一种草本植物,有其在药理上的价值,但如果肆无忌惮的大量使用,它就变成了毒品。

什么是静态类

所谓静态类指的是无需实例化成对象,直接通过静态方式调用的类。代码如下:

复制代码 代码如下:

<?php

class Math
{
    public static function ceil($value)
    {
        return ceil($value);
    }

    public static function floor($value)
    {
        return floor($value);
    }
}

?>

此时类所扮演的角色更像是命名空间,这或许是很多人喜欢使用静态类最直接的原因。

静态类的问题

本质上讲,静态类是面向过程的,因为通常它只是机械的把原本面向过程的代码集合到一起,虽然结果是以类的方式存在,但此时的类更像是一件皇帝的新衣,所以可以说静态类实际上是披着面向对象的壳儿,干着面向过程的事儿。

面向对象的设计原则之一:针对接口编程,而不是针对实现编程。这有什么不同?打个比方来说:抛开价格因素,你喜欢独立显卡的电脑还是集成显卡的电脑?我想绝大多数人会选择独立显卡。独立显卡可以看做是针对接口编程,而集成显卡就就可以看做是针对实现编程。如此说来针对实现编程的弊端就跃然纸上了:它丧失了变化的可能性。

下面杜撰一个文章管理系统的例子来具体说明一下:

复制代码 代码如下:

<?php

class Article
{
    public function save()
    {
        ArticleDAO::save();
    }
}

?>

Article实现必要的领域逻辑,然后把数据持久化交给ArticleDAO去做,而ArticleDAO是一个静态类,就好像焊在主板上的集成显卡一样难以改变,假设我们为了测试代码可能需要Mock掉ArticleDAO的实现,但因为调用时使用的是静态类的名字,等同于已经绑定了具体的实现方式,Mock几乎不可能,当然,实际上有一些方法可以实现:

复制代码 代码如下:

<?php

class Article
{
    private static $dao = 'ArticleDAO';

    public static funciton setDao($dao)
    {
        self::$dao = $dao;
    }

    public static function save()
    {
        $dao = self::$dao;

        $dao::save();
    }
}

?>

有了变量的介入,可以在运行时设定具体使用哪个静态类:

复制代码 代码如下:

<?php

Article::setDao('MockArticleDAO');

Article::save();

?>

虽然这样的实现方式看似解决了Mock的问题,但是首先它修改的原有的代码,违反了开闭原则,其次它引入了静态变量,而静态变量是共享的状态,有可能会干扰其它代码的执行,所以并不是一个完美的解决方案。

补充说明,利用动态语言的特性,其实可以简单的通过require一个不同的类定义文件来实现Mock,但这样做同样有弊端,设想我们在脚本里需要多次变换实现方式,但实际上我们只有一次require的机会,否则就会出现重复定义的错误。

对象的价值

如果放弃静态类,转而使用对象,应该如何实现文章管理系统的例子?代码如下:

复制代码 代码如下:

<?php

class Article
{
    private $dao;

    public function __construct($dao = null)
    {
        if ($dao === null) {
            $dao = new ArticleDAO();
        }

        $this->setDao($dao);
    }

    public function setDao($dao)
    {
        $this->dao = $dao;
    }

    public function save()
    {
        $this->dao->save();
    }
}

?>

实际上,这里用到了人们常说的依赖注入技术,通过构造器或者Setter注入依赖的对象:

复制代码 代码如下:

<?php

$article = new Article(new MockArticleDAO());

$article->save();

?>

对象有自己的状态,不会发生共享状态干扰其它代码的执行的情况。

当然,静态类有好的一面,比如说很适合实现一些无状态的工具类,但多数时候,我的主观倾向很明确,多用对象,少用静态类,避免系统过早的固化。顺便说一句,希望别有人告诉我静态类比对象快之类的说教,谢谢。

时间: 2024-10-28 05:56:27

基于PHP静态类的原罪详解_php实例的相关文章

基于PHP 面向对象之成员方法详解_php实例

用一个列子来读解成员方法:大家可以自己动手写一写,加深理解. 这样一个需求; 希望人可以说话, 做算术题.......,这样就需要使用到成员方法:1.添加speak 成员方法,输出 我是小明2.添加jisuan 成员方法,可以计算从 1+..+1000的结果3.修改jisuan 成员方法,该方法可以接收一个数n,计算 1+..+n 的结果4.添加add 成员方法,可以计算两个数的和 参考代码: 复制代码 代码如下: <?php     class Person{         public $

thinkPHP中钩子的两种配置调用方法详解_php实例

本文实例讲述了thinkPHP中钩子的两种配置调用方法.分享给大家供大家参考,具体如下: thinkphp的钩子行为类是一个比较难以理解的问题,网上有很多写thinkphp钩子类的文章,我也是根据网上的文章来设置thinkphp的钩子行为的,但根据这些网上的文章,我在设置的过程中,尝试了十几次都没有成功,不过,我还是没有放弃,最后还是在一边调节细节,一边试验的过程中实现了钩子行为的设置.下面是我个人的设置经验,在这里跟大家分享一下. 个人做了两种设置,都试验成功了,一个简单点,在thinkphp

Zend Framework入门应用实例详解_php实例

本文实例讲述了Zend Framework入门应用.分享给大家供大家参考,具体如下: .htaccess文件 .htaccess文件用来实现URL重置,即当用户访问某资源时,会将其重新定位到指定的文件下. 代码示例: RewriteEngine on RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php 其中,行1表示重置引擎打开,行2表示当访问除js.ico.gif.jpg.png.css以外的文件时, 都将被重置到index.php文件下. 注

Yii基于数组和对象的Model查询技巧实例详解_php实例

本文实例讲述了Yii基于数组和对象的Model查询技巧.分享给大家供大家参考,具体如下: 对于一个Model Post 有如下的4中查询方法,返回对象或者对象数组. //查找满足指定条件的结果中的第一行 find the first row satisfying the specified condition $post=Post::model()->find($condition,$params); //查找具有指定主键值的那一行 find the row with the specified

基于PHP CURL获取邮箱地址的详解_php实例

CURL可谓居家旅行必备之杀人良药,为何如此形容?就是因为他好用方便能实现页面抓取模拟登录采集等一系列功能.记得第一次接触CURL的时候是要实现完成从邮箱用户列表的抓取.当时为了赶进度没有细细研究只是网上找了一些资料实现了功能.现在把当初的代码整理一下功能依旧能用 复制代码 代码如下: <?php  error_reporting ( 0 );  set_time_limit ( 0 );  header ( "Content-Type: text/html; charset=GB2312

Symfony2安装第三方Bundles实例详解_php实例

本文实例讲述了Symfony2安装第三方Bundles的方法.分享给大家供大家参考,具体如下: 大多数的Bundles都提了安装的介绍,下面来介绍基本的安装步骤: 一.添加composer依赖关系 在symfony里,用composer来管理依赖关系 1.找到Bundle的包的名称 在包的README里一般都告诉了我们它的名称,如果没有,可以在https://packagist.org网站里搜索到 2.通过composer来安装Bundle 知道了bundle的包名之后,我们可以通过compos

Symfony2使用第三方库Upload制作图片上传实例详解_php实例

本文实例分析了Symfony2使用第三方库Upload制作图片上传的方法.分享给大家供大家参考,具体如下: 我们在应用程序或者网站的个人资料里一般都有设置头像的功能,这一章我们在Symfony2里用第三方的一个比较有名Upload库来制作上传图片的功能. 一.安装第三方库 1.在composer.json文件中的"require"中加入 "codeguy/upload": "*" 2.运行指令安装 composer update 二.编码 1.编

Yii CGridView用法实例详解_php实例

本文实例讲述了Yii CGridView用法.分享给大家供大家参考,具体如下: CGridView的功能是用来显示的数据列表.它支持排序,分页,和AJAX数据请求. CGridView最好使用 data provider,最好是 CActiveDataProvider . 简单代码如下: $dataProvider=new CActiveDataProvider('Post'); $this->widget('zii.widgets.grid.CGridView', array( 'dataPr

Laravel5权限管理方法详解_php实例

本文实例讲述了Laravel5权限管理的实现方法.分享给大家供大家参考,具体如下: 关于权限管理的思考 最近用laravel设计后台,后台需要有个权限管理.权限管理实质上分为两个部分,首先是认证,然后是权限.认证部分非常好做,就是管理员登录,记录session.这个laravel中也有自带Auth来实现这个.最麻烦就是权限认证. 权限认证本质上就是谁有权限管理什么东西.这里有两个方面的维度,谁,就是用户维度,在用户维度,权限管理的粒度可以是用户一个人,也可以是将用户分组,如果将用户分组,则涉及到