为什么单例在PHP环境中如此糟糕?(复制)

时间:2021-11-10 13:57:42

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:

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

I have a Request class:

我有一个请求类:

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

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

$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:

同样的美元_SESSION:

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

我有一个会话类(单例),它确实表示$_SESSION数组,因为只有一个会话,我在全局使用它。

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.

我还经常使用类来表示/使用db表的唯一行。

$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)

实际上,我的类中大多数(几乎所有)都没有公共构造函数,而是始终使用静态方法,如getInstance($id)或create(),这样类本身就可以处理可能的实例(这并不意味着它们都是单例)

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.

现在,您有了一个包装$_POST的单例,但是如果您没有$_POST,但是想要使用一个文件作为输入呢?在这种情况下,如果您有一个抽象的输入类,并实例化一个POSTInput以通过已发布的数据管理输入,将会更加方便。

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

但我仍然怀疑这种做法。假设我有一个“功能性”类,它执行一个具体的请求(比如一个guestbook组件)。在这个类中,我想获得一个send参数。因此我得到了请求的单例实例。

$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.

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

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?

当然,在创建功能时,我可以定义一个非可选参数。但这在某些时候会导致参数完全失效。或不呢?

2 个解决方案

#1


4  

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.

现在,您有了一个包装$_POST的单例,但是如果您没有$_POST,但是想要使用一个文件作为输入呢?在这种情况下,如果您有一个抽象的输入类,并实例化一个POSTInput以通过已发布的数据管理输入,将会更加方便。

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.

其他课程也一样。你不希望你的整个应用程序和这个MySQL单例。如果需要连接到两个MySQL数据库怎么办?如果您需要切换到WhatEverSQL呢?为这些类创建抽象,并覆盖它们以实现特定的技术。

#2


3  

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:

我认为单例对象不应该像在基于请求的体系结构(如PHP或ASP)中那样有不好的表现。NET(或任何你想要的)。本质上,在一个常规程序中,该单例程序的生命周期可以是程序运行时的几个月或几年:

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.

也就是说,对于基于请求的场景,比如PHP页面或ASP。NET页面,所有可调用的代码都被有效地封装在函数调用中。同样,他们混淆了全局变量(但在请求的上下文中),并设置了安全保护,防止多次创建。

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.

但我还是反对他们的使用。为什么?因为即使在您的单个请求的上下文中,所有内容都依赖于该实例并紧密耦合。当您希望使用不同的请求对象测试不同的场景时,会发生什么情况?假设您使用include进行编码,那么您现在必须去修改该调用的每个实例。如果您已经传递了对预先构造的请求类的引用,那么您现在可以做一些很酷的事情,比如提供类的模拟单元测试版本,只需更改传递给其他函数的内容。您还从使用这个通用请求对象中解耦了所有东西。

#1


4  

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.

现在,您有了一个包装$_POST的单例,但是如果您没有$_POST,但是想要使用一个文件作为输入呢?在这种情况下,如果您有一个抽象的输入类,并实例化一个POSTInput以通过已发布的数据管理输入,将会更加方便。

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.

其他课程也一样。你不希望你的整个应用程序和这个MySQL单例。如果需要连接到两个MySQL数据库怎么办?如果您需要切换到WhatEverSQL呢?为这些类创建抽象,并覆盖它们以实现特定的技术。

#2


3  

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:

我认为单例对象不应该像在基于请求的体系结构(如PHP或ASP)中那样有不好的表现。NET(或任何你想要的)。本质上,在一个常规程序中,该单例程序的生命周期可以是程序运行时的几个月或几年:

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.

也就是说,对于基于请求的场景,比如PHP页面或ASP。NET页面,所有可调用的代码都被有效地封装在函数调用中。同样,他们混淆了全局变量(但在请求的上下文中),并设置了安全保护,防止多次创建。

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.

但我还是反对他们的使用。为什么?因为即使在您的单个请求的上下文中,所有内容都依赖于该实例并紧密耦合。当您希望使用不同的请求对象测试不同的场景时,会发生什么情况?假设您使用include进行编码,那么您现在必须去修改该调用的每个实例。如果您已经传递了对预先构造的请求类的引用,那么您现在可以做一些很酷的事情,比如提供类的模拟单元测试版本,只需更改传递给其他函数的内容。您还从使用这个通用请求对象中解耦了所有东西。