(PHP)单例模式 怎么蹦出好多问题, 烦请大家帮忙指点一二

网络小白
  • 273

最近无意中看到一些对·单例模式·介绍的资料,有以下几个疑问:

  1. 很多资料说单例模式无法继承, 我自己觉得不对, 单例模式完全可以将private设置为protected, 然后将self设置为static来实现继承功能呀, 不知道说无法继承的是哪个角度思考的, 所以有疑问?
  2. 假设我的项目中有很多功能, 我的理解是 "用户每操作一下, 都会产生一个进程, 或者是一个进程中制作一次请求--相应操作, 然后当前操作在服务器运行完成并返回响应, 则进程结束, 这样的话, 假设你有个单例类是Log类, 用来记录日志, 那么本次进程结束后, 进程中的单例自然也就被销毁了, 另外一次操作的话, 这次操作会再次生成新的Log实例"

    • 所以单例应该是按一个个的进程为单位来讨论的, 但是很多资料说 "如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。" 这怎么理解? 多长时间算是长?
    • 毕竟我的项目根据功能点的不同, 可能有些功能点复杂, 有些简单, 我的Log实例要在每个功能中做日志记录, 难道还得考虑功能运行过长的话.... 我的日志会丢失一部分?
  3. 还有资料说:"滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出"

    • 数据库连接池一般不就是个类似数组的容器么? 我第一次建立好数据库连接之后, 将连接对象放入连接池中, 如果进程中再次需要连接数据库的话, 直接从连接池中取出之前创建好的连接对象不就行了(而且我看比如TP3.2就是这么做的)
    • 所谓 "连接池溢出" 是什么意思, 难道是说本次进程中有 n 个 不同的数据库连接, 导致连接池中存放数据对象过大导致的内存溢出?
    • 不应该吧, 如果连接池是数组, 能放不少数据的啊, 而什么业务能在一个进程中操作这么多不同的数据库连接, 所以还是没能理解
  4. TP3.2框架使用单例也是非完全套用 三私一公 来实现的, 比如ThinkDriverDb 只做到静态化所有方法即可, 你即使new出来也没用, 这样是不是也没什么槽点?
回复
阅读 3.1k
4 个回答
✓ 已被采纳

1.单例即类只有一个示例,如果继承,那就会产生多个实例,此时已经不是单例了。

2.理解差不多没错,单例解决的是同一个请求内,多处使用同一个类的问题。。比如一个请求内,可能要查十次数据库,单例就是保证这十次使用的是同一个数据库连接实例,而不是每次查询都实例化一次。

3.滥用单例可能导致连接池溢出,这个问题应该只存在于 java 中或类似得,猜测:因为java采用数据库连接池技术,因为java是持久化服务,滥用单例导致连接得不到释放,内存不断上升导致溢出。而php这种请求结束,所有连接及内存都会释放,不存在这个问题。

4.所有的设计模式都是为了要实现功能而服务,所以需要先看你想实现什么东西。灵活运用各种模式,以及其变种,才能写出更好的代码。。。

所以不用执着于你的四个问题是否有答案,知道有单例这个东西,知道多个示例与单个示例的区别,然后你的代码需要优化的时候,按照自己的需求来就行

命中水ヽ
  • 4.9k

首先,我们来看一个完整的规范的简单的单例模式的例子:

<?php

class User {
    //静态变量保存全局实例
    private static $_instance = null;
    //私有构造函数,防止外界实例化对象
    private function __construct() {
    }
    //私有克隆函数,防止外办克隆对象
    private function __clone() {
    }
    //静态方法,单例统一访问入口
    static public function getInstance() {
        if (is_null ( self::$_instance ) || !isset ( self::$_instance )) {
            self::$_instance = new self ();
        }
        return self::$_instance;
    }
    public function getName() {
        echo 'hello world!';
    }
}
$user = User::getInstance();
?>

下面回答问题:

1、你可以随便改你所谓的单例模式类,但是单例的核心是不能变的:产出一个实例;每次使用同一个实例对象;;如果你把private改成protected可以,但是这就无限实例了不是吗?不符合单例模式的设计规范呀;

2、这就是php的变量生命周期问题了。php所有的变量生命周期,一般情况下只存在于请求开始->请求结束,每次请求每个变量重新被赋值,每个对象被重新实例化;请求结束,自动释放;单例模式的的意义是:对象只被实例化一次,每次调用返回同一个实例;你的单例的生命周期只存在于这一请求过程中,请求结束,就没了。再次请求,从新生成新的单例;没毛病呀

3、滥用什么服务都会造成不好的影响;就你现在举这个例子而言,对数据库链接使用单例模式的话,每次使用的时候,都是用的同一静态内存区(类中static声明的变量默认保存在静态内存区中)的变量,所以只要实例化一次,之后在同一请求中使用的时候,就不会增加额外的数据库连接对象了。

而你说的连接池指的是数据库的连接池吧,这个连接池是有内存限制,也有最大,最小连接限制,而且是可以在mysql的配置文件中设置的。具体请参考《MySql数据库连接池专题》的介绍。

4、对TP的源码没看过,不方便回答。

以上。

seth-shi
  • 1.8k

clipboard.png

  1. 我是觉得可以继承的,使用static关键字

字太多,看得有点晕,你整理一下吧

<?php

//final防止类被继承
final class DBHelper{
    //初始化实例 含义:$install = new DBHelper()...
    private static $instance=null;
     
    //构造器私有,防止类外部实例化
    private function __construct(){
        //连接数据库操作
    }
     
    //获取实例
    public static function getInstance(){
        if(!(self::$instance instanceof self)){
            self::$instance=new self;
        }
         
        return self::$instance;
    }
     
    //防止实例被克隆
    private function __clone(){
    }
     
}    
     
 

?>

参考:http://blog.51cto.com/phpme/2...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏