为什么单例在 PHP 环境中这么糟糕?

发布于 2024-11-27 11:29:32 字数 1802 浏览 0 评论 0原文

可能的重复:
谁需要单例?

我想知道,在 php 脚本中使用单例有什么缺点。我经常使用它们,有时我无法理解对开发人员的批评。一些示例:

我有一个 Request 类:

清理 POST、GET、COOKIE 输入数据并使用它而不是全局数组 - 严格且全局。就像

$request = Request::getInstance();
$firstname = $request->post('firstname', $additionalFilters);

每个请求始终只有一个请求。为什么在这种情况下使用单例是一个坏主意?

$_SESSION 相同:

我有一个 Session 类(Singleton),它代表 $_SESSION 数组,因为只有一个会话,并且我在全局范围内使用它。

数据库

$mysql  = DB::getInstance('mysql', 'dbname'); //pseudo
$sqlite = DB::getInstance('sqlite', 'dbname'); //pseudo

对于每种类型的数据库,我只需要一个对象,而不需要更多。我认为否则存在混乱的风险。

唯一行

另外,我经常使用类来表示/使用数据库表的唯一行。

$article = Article::getInstance($id);
$navigation = Navigation::getInstance($id);

我认为这样做只有好处。我从不想要第二个对象代表唯一的行。为什么单例在这里是一个坏主意?

事实上,我的大多数(几乎所有)类都没有公共构造函数,但总是有一个静态方法,如 getInstance($id) 或 create(),因此类本身会处理可能的实例(这并不意味着它们都是单例)根据定义)

所以我的问题是:是否有任何我还没有意识到的缺点。当单身人士提出反对单身人士的建议时,他们会想到哪种具体情况。

编辑:

现在,你有了一个围绕 $_POST 的单例,但是如果你 没有 $_POST,但想使用文件作为输入?在那 如果你有一个抽象输入类会更方便, 并实例化 POSTInput 以通过发布的数据管理输入。

好的,有效的优势。我没有意识到这一点。特别是关于 Request 类的一点。

我仍然怀疑这种方法是否可行。假设我有一个“功能”类,它执行具体请求(如留言簿组件)。 在该类中我想获取一个发送的参数。所以我得到了 Request 的单例实例,

$req = Request::getInstance(); 
$message = $req->post('message');

这样,只有我的功能对象关心 Request 类。

当我使用非单例方法时,我需要一个额外的类/函数来管理每个请求都获得有效的请求对象。这样,我的功能类不需要了解该管理类,但在我看来,仍然会出现依赖性/问题:每次我创建功能对象的实例时,我都有可能忘记设置请求对象。

当然,我可以在创建功能时定义一个非可选参数。但这有时会导致参数过度杀伤。或不?

Possible Duplicate:
Who needs singletons?

i was wondering, what are the drawbacks using Singletons in php scripts. I use them alot and i sometimes can't understand criticism of developers. Some examples:

I have a Request class:

Sanitizing POST, GET, COOKIE inputdata and using it instead of the global arrays - strictly and globally. Like

$request = Request::getInstance();
$firstname = $request->post('firstname', $additionalFilters);

There is always only ONE request per request. Why is using singleton in this case a bad idea?

Same for $_SESSION:

I have a Session class (Singleton) which does represent the $_SESSION array because there is only one session and i use it globally.

Database

$mysql  = DB::getInstance('mysql', 'dbname'); //pseudo
$sqlite = DB::getInstance('sqlite', 'dbname'); //pseudo

For each type of database, i want only ONE object and never MORE. In my opinion there is otherwise a risk of chaos.

Unique rows

Also i often use classes to represent/use a unique row of a db table.

$article = Article::getInstance($id);
$navigation = Navigation::getInstance($id);

I see only benefits doing it this way. I never want a second object representing a unique row. Why is singleton such a bad idea here?

In fact, most (nearly all) my classes don't have a public constructor but always a static method like getInstance($id) or create() so the class itself handles the possible instances (which doesn't mean they are all singletons by definition)

So my question is: Are there any drawbacks i didn't realize yet. And which concrete scenario's the singleton-doubters thinking of when advise against Singletons.

Edit:

Now, you got a singleton that wraps around the $_POST, but what if you
don't have $_POST, but want to use a file for input instead? In that
case, it would be more convenient if you have an abstract input class,
and instantiate a POSTInput to manage input through posted data.

Ok, valid advantages. I didn't realized that. Especially the point regarding the Request class.

Still i have doubts whether that approach. Assume i have a "Functionality" class which executes a concrete request (like a guestbook component).
Within that class i want to get a sent parameter. So i get my singleton instance of Request

$req = Request::getInstance(); 
$message = $req->post('message');

This way, only my functionality object cares about a Request class.

When i use the non-singleton approach, i need somehow an additional class/function to manage that every request gets a valid request object. That way my Functionality class doesn't need to know about that managing class but in my opinion there still arises a dependence/problem: Everytime i create an instace of an Functionality object there is a chance that i forget to set a request object.

Surely i can define a non-optional parameter when creating a functionality. But that leads to a parameter overkill altogether at some time. Or not?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

百合的盛世恋 2024-12-04 11:29:32

单例(以及静态类,同样的情况在很大程度上适用)本身并不坏,但它们引入了您可能不想要的依赖项。

现在,您有了一个围绕 $_POST 的单例,但是如果您没有 $_POST,但想使用文件作为输入,该怎么办?在这种情况下,如果您有一个抽象输入类,并实例化一个 POSTInput 来通过发布的数据管理输入,那就更方便了。

如果您想从文件中获取输入,或者甚至(无论出于何种原因)想要根据数据库表的输入模拟(或重播)多个请求,您仍然可以执行此操作,而无需更改任何代码,除了实例化的部分之外班级。

这同样适用于其他类别。您不希望您的整个应用程序与此 MySQL 单例通信。如果您需要连接到两个 MySQL 数据库怎么办?如果您需要切换到 WhatEverSQL 怎么办?...为这些类型的类创建抽象,并重写它们以实现特定的技术。

Singletons (and static classes, to which the same story largely applies) are not bad per se, but they introduce dependencies that you may not want.

Now, you got a singleton that wraps around the $_POST, but what if you don't have $_POST, but want to use a file for input instead? In that case, it would be more convenient if you have an abstract input class, and instantiate a POSTInput to manage input through posted data.

If you want to get input from a file, or even (for whatever reason) want to mimic (or replay) multiple requests based on input from a database table, you can still do this, without altering any code, except the part that instantiates the class.

Same applies to other classes too. You don't want your whole application to talk to this MySQL singleton.. What if you need to connect to two MySQL databases? What if you need to switch to WhatEverSQL?.. Make abstracts for these kinds of classes, and override them to implement specific technologies.

一个人的夜不怕黑 2024-12-04 11:29:32

我认为单例不应该像在基于请求的架构(例如 PHP 或 ASP.NET(或任何你想要的))中那样受到负面报道。本质上,在常规程序中,该单例的生命周期可以与程序运行时的数月或数年一样多:

int main()
{
    while(dont_exit)
    {
        // do stuff
        Singleton& mySingleton = Singleton::getInstance();
        // use it
    }

    return 0;
}

除了只是一个全局变量之外,很难用单例来替换该单例这在单元测试中可能有用。可能依赖于它的代码量(可能有数百个源文件)将该单例的使用与整个程序紧密结合在一起。

也就是说,在基于请求的方案(例如 PHP 页面或 ASP.NET 页面)的情况下,所有可调用代码无论如何都有效地包装在函数调用中。同样,他们混淆了全局变量(但在请求的上下文中),并防止被多次创建。

但我仍然主张反对使用它们。为什么?因为即使在您的单个请求的上下文中,所有内容都依赖于该实例并与该实例紧密耦合。当您想使用不同的请求对象测试不同的场景时会发生什么?假设您使用包含进行编码,您现在必须去修改该调用的每个实例。如果您传递了对预先构造的 Request 类的引用,那么您现在可以做很酷的事情,例如通过简单地更改传递给其他函数的内容来提供类的模拟单元测试版本。您还使用这个通用 Request 对象将所有内容解耦。

I do not think singletons should have as bad press they do in a request-based architecture such as PHP or ASP.NET (or whatever you want). Essentially, in a regular program the life-time of that singleton can be as many months or years as the program is running:

int main()
{
    while(dont_exit)
    {
        // do stuff
        Singleton& mySingleton = Singleton::getInstance();
        // use it
    }

    return 0;
}

Apart from being little more than a global variable, it is very hard to replace that singleton with, perhaps, a singleton that might be useful in unit-testing. The amount of code that could depend on it, in potentially hundreds of source files tightly couples the use of that singleton to the entire program.

That said, in the case of request-based scenarios such as your PHP page, or an ASP.NET page, all your callable code is effectively wrapped in a function call anyway. Again, they are obfuscating a global variable (but within the context of the request) with safe-guards against being created more than once.

But still, I advocate against their use. Why? Because even in the context of your single request, everything is reliant and tightly coupled to that instance. What happens when you want to test a different scenario with a different request object? Assuming you coded using includes, you now have to go and modify every single instance of that call. If you had passed a reference to a pre-constructed Request class, you can now do cool stuff, such as provide mock unit-testing version of your class, by simply changing what gets passed down to the other functions. You've also de-coupled everything from using this universal Request object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文