ser*_*dev 136 php oop singleton design-patterns
我通过PDO访问我的MySQL数据库.我正在设置对数据库的访问权限,我的第一次尝试是使用以下内容:
我想到的第一件事是global:
$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'root', 'pwd');
function some_function() {
    global $db;
    $db->query('...');
}
这被认为是一种不好的做法.一点点搜索后,我结束了与Singleton模式,其
"适用于需要单个类实例的情况."
根据手册中的示例,我们应该这样做:
class Database {
    private static $instance, $db;
    private function __construct(){}
    static function singleton() {
        if(!isset(self::$instance))
            self::$instance = new __CLASS__;
        return self:$instance;
    }
    function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd')
        return self::$db;
    }
}
function some_function() {
    $db = Database::singleton();
    $db->get()->query('...');
}
some_function();
当我能做到这一点时,为什么我需要相对较大的课程呢?
class Database {
    private static $db;
    private function __construct(){}
    static function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd');
        return self::$db;
    }
}
function some_function() {
    Database::get()->query('...');
}
some_function();
这最后一个工作完美,我不再需要担心$db.
我怎样才能创建一个较小的单例类,或者是否存在我在PHP中缺少的单例的用例?
Gor*_*don 318
在对象存在于共享内存中的语言中,单例可用于保持较低的内存使用率.您可以从全局共享的应用程序内存中引用现有实例,而不是创建两个对象.在PHP中没有这样的应用程序内存.在一个请求中创建的Singleton仅适用于该请求.在另一个同时完成的请求中创建的Singleton仍然是一个完全不同的实例.因此,Singleton的两个主要目的之一在这里不适用.
此外,许多在概念上只能在应用程序中存在一次的对象不一定需要语言机制来强制执行此操作.如果只需要一个实例,则不要实例化另一个实例.只有当您没有其他实例时,例如当您创建第二个实例时小猫死亡时,您可能拥有Singleton的有效用例.
另一个目的是在同一个请求中拥有一个实例的全局访问点.虽然这听起来可取,但实际上并非如此,因为它会创建与全局范围的耦合(如任何全局和静态).这使得单元测试更加困难,并且您的应用程序通常难以维护.有一些方法可以缓解这种情况,但一般来说,如果需要在许多类中使用相同的实例,请使用依赖注入.
请参阅我在PHP中的Singletons幻灯片- 为什么它们很糟糕,以及如何从应用程序中删除它们以获取更多信息.
即便是单身人士模式的发明者之一埃里希·伽玛(Erich Gamma),现在对这种模式表示怀疑:

Bil*_*l K 79
好吧,当我第一次开始职业生涯时,我想知道那一段时间.以不同的方式实现它,并提出了两个选择不使用静态类的理由,但它们非常大.
一个是你经常会发现一些东西,你绝对肯定你永远不会有一个以上的实例,你最终会有一个.你最终可能会得到第二台显示器,第二台数据库,第二台服务器 - 无论如何.
当发生这种情况时,如果你使用了一个静态类,你所使用的重构比使用单例时更糟糕.单例本身就是一个iffy模式,但它很容易转换为智能工厂模式 - 甚至可以转换为使用依赖注入而不会有太多麻烦.例如,如果你的单例是通过getInstance()获得的,你可以很容易地将它改为getInstance(databaseName)并允许多个数据库 - 没有其他代码更改.
第二个问题是测试(老实说,这与第一个问题相同).有时您希望用模拟数据库替换数据库.实际上,这是数据库对象的第二个实例.对于静态类而言,这比使用单例更难实现,您只需要模拟getInstance()方法,而不是静态类中的每一个方法(在某些语言中可能非常困难).
这实际上取决于习惯 - 当人们说"全球"很糟糕时,他们有很好的理由说出来,但在你自己解决问题之前,这可能并不总是显而易见的.
你可以做的最好的事情就是问(像你一样)然后做出选择并观察你的决定的后果.拥有解释代码随时间演变的知识比首先做正确的知识要重要得多.
uni*_*100 21
谁需要PHP中的单身人士?
请注意,几乎所有对单身人士的反对意见都来自技术观点 - 但它们的范围也非常有限.特别是对于PHP.首先,我将列出使用单身人士的一些原因,然后我将分析对单身人士使用的反对意见.首先,需要它们的人:
- 编写大型框架/代码库的人员将在许多不同的环境中使用,他们必须使用以前存在的不同框架/代码库,必须实现来自客户/老板的许多不同的,变化的,甚至是异想天开的请求/管理/单位领导.
看,单身模式是自包容的.完成后,单个类在您包含它的任何代码中都是严格的,它的行为与您创建方法和变量的方式完全相同.它在给定的请求中始终是相同的对象.因为它不能被创建两次成为两个不同的对象,所以你知道单个对象在代码中的任何给定点是什么 - 即使将单例插入到两个,三个不同的,旧的,甚至意大利面条代码库中.因此,它在开发方面更容易 - 即使有很多人在该项目中工作,当您在任何给定的代码库中看到单点被初始化时,您知道它是什么,它做什么,如何它和它所处的状态.如果它是传统的类,你需要跟踪该对象最初创建的位置,在代码中的那一点及其特定状态之前调用了哪些方法.但是,在那里放一个单例,如果你在编码时放弃了正确的调试和信息方法并跟踪到单例,你就知道它到底是什么.因此,它使那些必须使用不同代码库的人更容易,需要集成以前用不同的哲学完成的代码,或者由你没有联系的人完成.(即,供应商 - 项目 - 公司 - 不管是什么,没有任何支持).它使得那些必须使用不同代码库的人更容易,需要集成以前用不同的哲学完成的代码,或者由你没有联系的人完成.(即,供应商 - 项目 - 公司 - 不管是什么,没有任何支持).它使得那些必须使用不同代码库的人更容易,需要集成以前用不同的哲学完成的代码,或者由你没有联系的人完成.(即,供应商 - 项目 - 公司 - 不管是什么,没有任何支持).
- 需要使用第三方API,服务和网站的人员.
如果你仔细观察,这与前面的情况并没有太大区别 - 第三方API,服务,网站就像外部的,孤立的代码库,你无法控制它们.任何事情都可能发生.因此,使用单例会话/用户类,您可以从第三方提供商(如OpenID,Facebook,Twitter等)管理任何类型的会话/授权实施- 您可以同时从SAME单例对象执行这些操作 - 无论您插入什么代码,都可以在任何给定点以已知状态轻松访问.您甚至可以为自己的网站/应用程序中的SAME用户创建多个不同的第三方API /服务的会话,并执行您想要做的任何事情.
当然,所有这些也可以通过使用普通类和对象来传统方法 - 这里的问题是,单例更整洁,更整洁,因此与在这种情况下的传统类/对象使用相比,这更易于管理/测试.
- 需要快速发展的人
单例的全局性行为使得构建任何类型的代码变得更容易,其中框架具有要构建的单例集合,因为一旦你很好地构建了单例类,已经建立的,成熟的和集合的方法将很容易获得,随时随地以一致的方式使用.成熟课程需要一些时间,但在此之后,它们坚如磐石,一致且有用.你可以在单例中使用尽可能多的方法来做任何你想要的事情,虽然这可能会增加对象的内存占用,但它可以为快速开发带来更多的时间节省 - 在一个给定的实例中你没有使用的方法一个应用程序可以在另一个集成的应用程序中使用,你可以打一个新的功能,客户端/老板/项目经理只需要做一些修改.
你明白了.现在让我们继续反对单身人士和邪恶的十字军反对有用的东西:
- 最重要的是它使测试更难.
实际上,它在某种程度上确实存在,即使通过采取适当的预防措施并将调试例程编码到您的单例中可以很容易地减轻它,并意识到您将调试单例.但是请注意,这与其他任何编码哲学/方法/模式都不同 - 只是因为,单体相对较新且不普遍,因此目前的测试方法最终与它们不相容.但是在编程语言的任何方面都没有区别 - 不同的风格需要不同的方法.
有一点,这种反对意见不明确,它忽略了这样一个事实,即应用程序开发的原因不是"测试",测试不是进入应用程序开发的唯一阶段/过程.应用程序开发用于生产用途.正如我在"谁需要单身人士"部分中所解释的那样,单身人士可以从必须使代码工作的复杂性和内部许多不同的代码库/应用程序/第三方服务中获得巨大优惠.在测试中可能丢失的时间是在开发和部署中获得的时间.这在第三方认证/应用程序/集成的时代特别有用 - Facebook,Twitter,OpenID等等,谁知道接下来会发生什么.
虽然这是可以理解的 - 程序员根据他们的职业生涯在非常不同的环境中工作.对于那些在相对较大的公司工作的人来说,他们以一种舒适的方式管理不同的,定义的软件/应用程序,而且没有即将到来的预算削减/裁员和随之而来的需要做很多不同的事情.一个便宜/快速/可靠的时尚,单身人士似乎不是那么必要.它甚至可能对他们已经拥有的东西造成滋扰/阻碍.
但是对于那些需要在"敏捷"开发的肮脏战壕中工作的人,不得不从他们的客户/经理/项目中实施许多不同的请求(有时是不合理的),由于前面解释的原因,单身人士是一个拯救的恩典.
- 另一个反对意见是它的内存占用更高
因为每个客户端的每个请求都会存在一个新的单例,这可能是PHP的反对意见.对于构造不良和使用的单例,如果应用程序在任何给定点为许多用户提供服务,则应用程序的内存占用量可能会更高.
但是,这对于您在编写内容时可以采用的任何方法都是有效的.应该提出的问题是,这些单身人士持有和处理的方法,数据是否必要?因为,如果它们在应用程序获得的许多请求中是必需的,那么即使您不使用单例,这些方法和数据也将通过代码以某种形式出现在您的应用程序中.因此,当您将传统的类对象1/3初始化为代码处理并将其摧毁到3/4时,这一切都成为一个问题,即您将节省多少内存.
当看到这种方式时,问题就变得无关紧要了 - 不管你是否使用单例,都不应该有不必要的方法,代码中的对象中保存的数据.因此,对单身人士的这种反对变得非常有趣,因为它假定在您使用的类中创建的对象中将存在不必要的方法,数据.
- 一些无效的反对意见,例如"使得维护多个数据库连接变得不可能/更难"
我甚至不能开始理解这个异议,当所有人都需要维护多个数据库连接,多个数据库选择,多个数据库查询,给定单例中的多个结果集时,只需将它们保存在单例中的变量/数组中即可他们是需要的.这可以像将它们保存在数组中一样简单,尽管您可以创建任何您想要使用的方法来实现它.但是让我们检查最简单的情况,在给定的单例中使用变量和数组:
想象一下,下面是给定的数据库单例:
$ this - > connections = array(); (错误的语法,我只是这样输入它给你的图片 - 变量的正确声明是public $ connections = array();它的用法是$ this-> connections ['connectionkey']自然)
您可以以这种方式在阵列中的任何给定时间设置并保持多个连接.对于查询,结果集等也是如此.
$ this - > query(QUERYSTRING,'queryname',$ this-> connections ['particulrconnection']);
这可以只使用选定的连接查询选定的数据库,只需存储在您的
$ this - >结果
使用键'queryname'的数组.当然,您需要为此编写查询方法 - 这很简单.
这使您可以根据需要维护几乎无限数量(当然,资源限制允许的数量)不同的数据库连接和结果集.并且它们可以在任何给定代码库中的任何给定点中的任何代码段中使用,该单例类已经被实例化.
当然,你自然需要在不需要的时候释放结果集和连接 - 但这不用说,并且它不是特定于单身人士或任何其他编码方法/风格/概念.
此时,您可以看到如何在同一单例中维护与第三方应用程序或服务的多个连接/状态.没那么不同.
简而言之,最终,单身模式只是编程的另一种方法/风格/哲学,当它们以正确的方式在正确的位置使用时,它们与任何其他模式一样有用.这与任何事都没有什么不同.
你会注意到,在大多数单身人士遭受抨击的文章中,你也会看到"全局"被称为"邪恶"的引用.
让我们面对现实吧 - 任何使用不当,滥用,滥用,邪恶的东西.这不限于任何语言,任何编码概念,任何方法.每当你看到有人发布诸如"X是邪恶的"的一揽子陈述时,就要远离那篇文章.机会非常高,它是有限观点的产物 - 即使观点是多年来某些特定经验的结果 - 这通常最终导致在特定的风格/方法中工作太多 - 典型的知识保守主义.
可以给出无尽的例子,从"全球化是邪恶的"到"非法是邪恶的".大约10年前,甚至在任何给定的应用程序中提议使用iframe都是异端邪说.然后是Facebook,iframe到处都是,看看发生了什么 - iframe不再那么邪恶了.
仍然有人顽固地坚持认为他们是"邪恶的" - 有时也是有充分理由的 - 但是,正如你所看到的那样,有需要,iframe填补了这种需要并且运作良好,因此整个世界都在继续前进.
程序员/编码人员/软件工程师最重要的资产是自由,开放和灵活的头脑.
在你的例子中,你正在处理一段看似不变的信息.对于这个例子,Singleton会有点过分,只是在类中使用静态函数就可以了.
更多的想法:你可能正在经历一个为了模式而实施模式的案例,而你的直觉告诉你"不,你不必"因为你拼出的原因.
但是:我们不知道您项目的规模和范围.如果这是简单的代码,也许扔掉,那可能不需要改变然后是,继续使用静态成员.但是,如果您认为您的项目可能需要扩展或准备进行维护编码,那么,是的,您可能希望使用Singleton模式.
首先,我只想说我对Singleton模式没有太多用处.为什么要在整个应用程序中保留单个对象?特别是对于数据库,如果我想连接到另一个数据库服务器怎么办?我每次都要断开连接并重新连接......?无论如何...
在应用程序中使用全局变量有几个缺点(这是Singleton模式的传统用法):
使用静态类而不是单例实例也提供了一些相同的缺点,因为单例的最大问题是静态getInstance方法.
您可以在不使用传统getInstance方法的情况下限制类可以拥有的实例数:
class Single {
    static private $_instance = false;
    public function __construct() {
        if (self::$_instance)
           throw new RuntimeException('An instance of '.__CLASS__.' already exists');
        self::$_instance = true;
    }
    private function __clone() {
        throw new RuntimeException('Cannot clone a singleton class');
    }
    public function __destruct() {
        self::$_instance = false;
    }
}
$a = new Single;
$b = new Single; // error
$b = clone($a); // error
unset($a);
$b = new Single; // works
这将有助于上面提到的第一点:单元测试和依赖注入; 同时仍然确保您的应用程序中存在该类的单个实例.举例来说,您可以将生成的对象传递给模型(MVC模式)供他们使用.
简单地考虑一下您的解决方案与PHP文档中提供的解决方案的不同之处.实际上,只有一个"小"差异:您的解决方案为getter的调用者提供了一个PDO实例,而docs中的那个提供Database::singleton了一个Database实例的调用者(然后他们使用getter来获取PDO实例).
那么我们得出的结论是什么?
Database实例.该Database班可暴露(事实上,它应该比一个更丰富或更高级别的接口,如果你打算到这一切的麻烦揭露),PDO它包装的对象.PDO,那么这两个实现是等效的.遵循手动实施没有任何好处.在实践方面,Singleton是一个非常有争议的模式.这主要是因为:
所以,作为最后的结论:你的单身就好了.根本不使用Singleton也很好.
| 归档时间: | 
 | 
| 查看次数: | 39837 次 | 
| 最近记录: |