静态功能很糟糕 - 但是替代方案是什么?

时间:2015-10-29 08:34:58

标签: php oop static yii2

在我的示例中,我使用的是PHP框架Yii2,但我认为这适用于大多数OO语言。

我有一个ActiveRecord基类,我的大部分业务对象都是从Project

目前,如果我想要一个Project实例,我可以打电话

Project::findOne(['id' => $id]);

findOne是ActiveRecord的静态方法(它是Yii2框架的一部分)。所以这是糟糕的形式,因为在编写单元测试时我不能轻易地模拟/存根这个调用的返回。

但是解决这个问题的最佳方法是什么?

我可以创建一个继承自CActiveRecord的类ActiveRecord,并在非静态调用中包装静态调用并在任何地方使用它 - 但是我必须实例化一次丢弃{{} 1}}对象以获取实际实例。如果Project对象需要一些繁重的配置来实例化 - 我会将随机的废话传递给构造函数只是为了得到一个实例。

要点: 简单地将静态更改为非静态似乎是错误的 - 我不应该将函数移动到其他地方吗?如果是这样,在哪里?

1 个答案:

答案 0 :(得分:7)

静态调用的问题是硬耦合到特定的其他代码段。只需将其包裹在一个动态的"电话并没有让这更好:

$c = new CProject;
$c->findOne(); // Calls Project::findOne()

这非常毫无意义。问题不在于->::的语法,问题在于此特定代码引用了特定的其他类,并且您无法轻松地将此类替换为其他类。您在类/对象之间构建严格的,硬编码的依赖关系,这使得将它们分开很难,这使得您的代码难以测试,并且使得代码难以适应不同的代码的情况。

替代方案是依赖注入

function foo(Project $project) {
    $p = $project->findOne();
}

此函数未与任何一个特定的 Project类耦合,而是与仅提供类似于Project的接口的类耦合。事实上,Project甚至可能只是interface。在这里调用哪个特定的类和方法然后在某处完全不同,就像你的依赖注入容器一样;或者只是这段代码的来电者。

这使得将此代码分开并以不同方式将其重新组合在一起变得更加容易,这对于手头的情况是必要的。这并不是说它不起作用,你根本不应该使用静态调用,但你真的需要知道你用每个硬编码的类名建立的交叉依赖关系,以及是否可能会或可能不会导致问题。对于即使是中等复杂和/或增长的软件项目,它 几乎肯定会以某种形式引起摩擦。

请参阅How Not To Kill Your Testability Using Statics了解更深入的文章。