静态函数不好-但是有什么替代方法? [英] Static functions are bad - but what's the alternative?

查看:118
本文介绍了静态函数不好-但是有什么替代方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In my example I'm using the PHP framework Yii2 but I think this applies to most OO languages.

我有一个ActiveRecord基类,我的大多数业务对象都从该基类扩展而来. Project.

I have an ActiveRecord base class which most of my business objects extend from e.g. Project.

此刻,如果我要呼叫Project实例

At the moment if I want a Project instance I call

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

findOne是ActiveRecord的静态方法(它是Yii2框架的一部分).因此,这是一种不好的形式,因为在编写单元测试时,我无法轻松地模拟/添加此调用的返回值.

findOne is a static method of ActiveRecord (which is part of the Yii2 framework). So this is bad form because I can't easily mock/stub the return of this call when writing unit tests.

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

But what's the best way to get around this?

我可以创建一个从ActiveRecord继承的类CActiveRecord并将静态调用包装在一个非静态调用中,并在各处使用它-但随后我必须按顺序实例化一个废弃的Project对象获取实际实例.如果Project对象需要实例化一些繁重的配置怎么办-我将把随机的废话传递给构造函数只是为了获得一个实例.

I could create a class CActiveRecord that inherits from ActiveRecord and wrap the static call in a non-static call and use that everywhere - but then I would have to instantiate a throw-away Project object in order to get the actual instance. What if the Project object needed some heavy config to be instantiated - I would be passing random nonsense into the constructor just to get an instance.

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

推荐答案

静态调用的问题是与其他特定代码的硬耦合.仅仅将其包装在动态"调用中并不能使它变得更好:

The issue with static calls is the hard coupling to a specific other piece of code. Just wrapping that in a "dynamic" call doesn't make this any better:

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

那真是毫无意义.问题不是->相对于::的语法,问题在于此特定代码引用了特定的其他类,并且您不能轻易地将该类换成其他东西.您正在类/对象之间建立严格的,硬编码的依赖项,这使得很难将它们拆开,这使您的代码难以测试,并且使代码难以适应不同的情况.

That's pretty darn pointless. The issue is not the syntax of -> vs. ::, the issue is that this particular code references a specific other class and that you cannot easily exchange this class for something else. You're building rigid, hardcoded dependencies between your classes/objects, which makes it hard to take them apart, which makes your code hard to test, and which makes it harder to adapt code to different situations.

替代方法是依赖注入:

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

此函数未与任何 Project类耦合,而是与仅提供类似于Project的接口的类耦合.实际上,Project甚至可以只是一个interface.然后,在哪个地方调用完全不同的特定类和方法,就像在您的依赖注入容器中一样.或仅仅是此代码的调用者.

This function is not coupled to any one specific Project class, but to a class which simply offers an interface akin to Project. In fact, Project could even be simply an interface. Which specific class and method is getting called here then is decided somewhere completely different, like your dependency injection container; or simply the caller of this code.

这使得将这些代码拆开并根据当前情况的需要以不同的方式将其重新组合起来变得容易得多.这并不是说它不起作用,并且您绝对不要使用静态调用,但是您确实需要知道您正在使用每个硬编码的类名建立的交叉依赖关系,以及这是否会导致一个问题.对于甚至相当复杂和/或不断增长的软件项目, 几乎肯定会最终导致某种形式的摩擦.

This makes it a lot easier to take this code apart and put it back together in different ways, as necessary for the situation at hand. That's not to say it can't work and that you should never use static calls at all, but you really need to be aware of what cross-dependencies you're establishing with every hardcoded class name, and whether that may or may not cause a problem down the line. For even moderately complex and/or growing software projects, it will almost certainly cause friction in some form or another eventually.

请参阅如何不使用静态功能杀死可测试性,以获取更深入的文章.

See How Not To Kill Your Testability Using Statics for a longer in-depth article.

这篇关于静态函数不好-但是有什么替代方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆