`

静态类的原罪

 
阅读更多

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

什么是静态类

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class Math
{
public static function ceil($value)
{
return ceil($value);
}
public static function floor($value)
{
return floor($value);
}
}
?>

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

静态类的问题

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

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

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

1
2
3
4
5
6
7
8
9
10
11
<?php
class Article
{
public function save()
{
ArticleDAO::save();
}
}
?>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Article
{
private static $dao = 'ArticleDAO';
public static funciton setDao($dao)
{
self::$dao = $dao;
}
public static function save()
{
$dao = self::$dao;
$dao::save();
}
}
?>

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

1
2
3
4
5
6
7
<?php
Article::setDao('MockArticleDAO');
Article::save();
?>

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

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

注:某些情况下,利用静态延迟绑定也可以提高静态类的可测试性,参考PHPUnit

对象的价值

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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注入依赖的对象:

1
2
3
4
5
6
7
<?php
$article = new Article(new MockArticleDAO());
$article->save();
?>

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

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

(转载至微信:PHP老杨,供学习参考之用)

分享到:
评论

相关推荐

    .NET :静态类的理解

    .NET 框架中的静态类是一种特殊的类,它在编程中起到了重要的作用,尤其是在需要提供全局访问点或者无状态的功能时。静态类的关键特征在于它们是通过 `static` 关键字声明的,并且只能包含静态成员,即静态字段、...

    MSIL 静态类在 IL 定义上和非静态类的差别.rar

    **标题解析:** "MSIL 静态类在 IL 定义上和非静态类的差别" 在.NET框架中,Microsoft Intermediate Language (MSIL) 是编译器生成的中间语言,它作为C#, VB.NET等高级语言与硬件平台之间的桥梁。本主题将探讨在...

    c#面向对象静态类、构造方法、静态方法介绍.zip

    在C#编程中,面向对象是核心特性之一,它提供了静态类、构造方法以及静态方法等概念,这些是创建高效、可维护代码的关键元素。让我们深入探讨这些知识点。 首先,静态类是C#中不能被实例化的类,它们只包含静态成员...

    php中静态类与静态变量用法类.zip

    直接调用类方法量:class::attribute/function,无论是静态/非静态都可以,静态static:声明类成员或方法为 static,就可以不实例化类而直接访问,不能通过一个对象来访问其中的静态成员(静态方法除外),静态成员属于类,不...

    JS定义静态类

    ### JS定义静态类 在JavaScript中,静态类的概念与传统面向对象编程语言中的类有所区别。JavaScript本身是一种基于原型的动态语言,直到ES6(ECMAScript 2015)引入了类语法,才使得类的概念更加清晰。不过,这里的...

    Visual Studio2012链接MySQL的静态类

    本教程将详述如何在Visual Studio 2012中利用静态类来链接MySQL数据库,以便进行数据操作。我们将主要围绕以下知识点展开: 1. **Visual Studio 2012与MySQL的集成**: Visual Studio 2012本身并不直接支持MySQL...

    wmp播放器静态类模块.rar

    《易语言WMP播放器静态类模块解析及应用》 Windows Media Player(WMP)是微软公司开发的一款功能强大的媒体播放器,它支持多种音频、视频格式,深受用户喜爱。在编程领域,开发者们常常利用WMP的API或类库来实现...

    ConsoleTest2 C#基础静态类 实例类.rar

    在C#编程语言中,静态类和实例类是两种不同的类类型,它们在程序设计中扮演着不同的角色。本教程将深入探讨这两种类的概念、特点以及如何在C#项目中应用它们。 首先,我们来理解静态类。在C#中,静态类是一个不能被...

    一个 自己 写的 jar 包,含有 Java 调用 Liunx 的静态类;文件的创建,读取,上传的静态类,和筛选目录下文件的静态类

    这个jar包包含了一系列用于操作文件和与Linux系统交互的Java静态类。让我们深入探讨这些知识点。 首先,关于Java调用Linux的静态类,这通常涉及到Java的Runtime类、ProcessBuilder类或者Runtime.exec()方法。这些...

    .net中数据表的静态类方法

    针对SQL数据操作,定义数据表为静态类和公共属性,实现通用的增删改查的操作,同时定义了自增字段及键值字段,自动实现更新或添加。十分方便的建立一个数据表的对象,使数据操作变得简单。

    PHP生成静态类.doc

    这个类是静态类的一个示例,尽管在实际代码中并未明确声明为`static`。下面我们将详细探讨如何使用PHP创建和使用静态类,以及在给定的示例中`template`类的工作原理。 1. 静态类的定义: 在PHP中,要创建一个静态类...

    浅析C#静态类,静态构造函数,静态变量

    C#中的静态类、静态构造函数和静态变量是编程中重要的概念,它们在程序设计中起着关键的作用。 首先,让我们来理解静态类。在C#中,静态类是一种特殊的类,它不能被实例化,只能通过类名直接调用其静态成员。静态类...

    JavaScript面向对象之静态与非静态类

    在JavaScript中,我们可以通过构造函数和原型来创建类,而类的成员可以分为静态成员和非静态成员。 静态类和非静态类的概念主要来源于静态类型的语言,如Java或C#。在JavaScript中,我们没有严格意义上的静态类,但...

    .net core 静态类获取appsettings的方法

    本篇文章将深入探讨如何通过静态类来获取appsettings.json中的配置信息,这对于构建可复用且易于访问的配置代码非常有帮助。 首先,让我们了解.NET Core中获取appsettings.json配置的基本方法。这通常通过依赖注入...

    易语言wmp播放器静态类模块

    易语言wmp播放器静态类模块源码,wmp播放器静态类模块,方法_创建,方法_关闭,读_属性_地址,写_属性_地址,读_属性_打开状态,读_属性_播放状态,方法_取控制,方法_取设定,方法_取当前媒体,方法_置当前媒体,...

    静态内部类

    ### 静态内部类详解 #### 一、静态内部类概述 静态内部类(Static Inner Class)是在Java中一种特殊的内部类实现形式。在Java编程语言中,内部类允许在一个类的内部定义另一个类。根据内部类是否声明为static,...

    面向对象模拟静态类和构造对象

    面向对象模拟静态类和构造对象面向对象模拟静态类和构造对象面向对象模拟静态类和构造对象面向对象模拟静态类和构造对象

Global site tag (gtag.js) - Google Analytics