为什么单身人士在PHP环境中如此糟糕?

时间:2011-08-01 16:55:20

标签: php design-patterns singleton

  

可能重复:
  Who needs singletons?

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

我有一个请求类:

消毒POST,GET,COOKIE inputdata并使用它而不是全局数组 - 严格地和全局地。像

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

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

$ _SESSION相同:

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

数据库

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

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

唯一行

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

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

我认为这样做只会带来好处。我从不想要第二个对象代表一个独特的行。为什么单身人士这么糟糕呢?

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

所以我的问题是:我还没有意识到任何缺点。具体情况是单身怀疑者在对Singletons提出建议时的想法。

修改

  

现在,你有一个包裹在$ _POST周围的单身人士,但如果你这样做了   没有$ _POST,但想要使用文件进行输入?在那里面   如果你有一个抽象的输入类,会更方便   并实例化POSTInput以通过发布的数据管理输入。

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

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

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

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

当我使用非单例方法时,我需要以某种方式管理每个请求获得有效请求对象的附加类/函数。这样我的功能类不需要知道那个管理类,但在我看来仍然会出现依赖/问题:每次我创建一个功能对象的实例时,我有可能忘记设置一个请求对象。

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

2 个答案:

答案 0 :(得分:4)

单身人士(以及同一故事大部分适用的静态课程)本身并不坏,但他们会引入你可能不想要的依赖关系。

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

如果你想从文件中获取输入,或者甚至(无论出于何种原因)想要根据数据库表的输入模仿(或重放)多个请求,你仍然可以这样做,而不需要改变任何代码,除了实例化该类的部分。

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

答案 1 :(得分:3)

我不认为单身人士在基于请求的体系结构(例如PHP或ASP.NET(或任何你想要的))中应该有那么糟糕的新闻。基本上,在常规程序中,该单例的生命周期可以是程序运行的数月或数年:

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

    return 0;
}

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

也就是说,对于基于请求的方案(例如PHP页面或ASP.NET页面),无论如何,所有可调用代码都有效地包含在函数调用中。同样,他们正在混淆全局变量(但在请求的上下文中),并且不会多次创建安全保护。

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