php面向对象——单例模式

定义

  • 保证一个类只有一个实例,并提供一个访问它的全局访问点。

应用场景

  • 多人协同开发,要调用mysql类的实例对象,应避免大量的new 操作消耗的资源
  • 数据库的链接,很耗资源,现在网站的瓶颈有两个,第一个是带宽,第二个是数据库。数据库的链接原则,能不连就不连
  • 如何限制,让多人开发,无论你怎么操作,只能得到一个对象呢?
  • 解决:使用单例模式,确保一个类只有一个实例

主要思路

  1. 保护或私有构造函数,防止外部实例化
  2. 内部开放一个公共的静态方法,负责实例化
  3. 类内有一个静态私有属性存放对象
  4. 检测,若静态私有属性已经存放对象,直接return该属性

实现历程

第一步:一个普通的类

  • 这个普通类,可以new来实例化,显然不是单例
  • 解决思路:不让new了
class Single{ 
    function __construct(){
    }
}
$s1 = new Single();
$s2 = new Single();

第二步:既然是new实例化了多个对象,那就不让new了

  • 把构造方法保护/私有
  • 确实外部不能new了,但是得不到对象了,这不叫单例,叫0例,哈哈~
  • 解决思路:写一个静态方法,在方法内部new对象,在类内调用构造方法
class Single{
    private function __construct(){
    }
}
$s1 = new Single();     // 报错  私有方法不能在类{}外调用

第三步:通过static方法在类内部new对象

  • 写一个获取实例对象的方法,return对象
  • 如何调用这个方法,只能把这个方法设置为静态公共(因为要在类外静态调用)
  • 虽从内部new,但仍然生成了不同对象,如何控制只能new一次
  • 解决思路:写一个静态属性instance,把内部new到的对象return到instance存起来,然后做判断
class Single{
    public $hash;  // 随机码
    private function __construct(){
        $this->hash = mt_rand(1,99999);
    }
    static public function getInstance(){
        return new Single();
    }
}

$s1 = Single::getInstance();
$s2 = Single::getInstance();
print_r($s1);   //  [hash] => 12904
print_r($s2);   //  [hash] => 99339
if ($s1 === $s2){
    echo '是同一个对象';
}else{
    echo '不是一个对象';  // 不是一个对象
}

第四步:通过static方法在类内部new对象,并且把对象保存到类内部的静态属性

  • 写一个静态私有属性,将对象保存到该属性(只能静态调用,并要防止在类外修改该属性)
  • 判断$instance是否是Single类的实例,来控制是否new对象,确保只有一个对象
  • 至此:一个简陋的单例就做好了
class Single{
    public $hash;  // 随机码
    static private $instance = null;
    private function __construct(){
        $this->hash = mt_rand(1,99999);
    }
    static public function getInstance(){
        if (! self::$instance instanceof Single){   // instanceof判断某个对象是不是某个类的实例
            self::$instance = new Single();
        }return self::$instance;

    }
}

$s1 = Single::getInstance();
$s2 = Single::getInstance();
print_r($s1);   //  [hash] => 6556
print_r($s2);   //  [hash] => 6556
if ($s1 === $s2){
    echo '是同一个对象';  // 是同一个对象
}else{
    echo '不是一个对象';
}

漏洞1 : 辛苦写好的单例 继承一下 就不灵了

  • 继承,然后再写一个同名公共构造函数,又可以new对象了
  • 父类的构造函数为私有,由父类的标志,不是不能重写吗?
  • 子类中public function __construct(),这不是在重写,子类也没权限重写,这是写了个同名的构造函数
  • 解决方法:用final修饰父类构造函数 能继承 不能重写
class Single{
    public $hash;  // 随机码
    static private $instance = null;
    private function __construct(){
        $this->hash = mt_rand(1,99999);
    }
    static public function getInstance(){
        if (! self::$instance instanceof Single){   // instanceof判断某个对象是不是某个类的实例
            self::$instance = new Single();
        }return self::$instance;
    }
}

$s1 = Single::getInstance();
$s2 = Single::getInstance();
print_r($s1);   //  [hash] => 43232
print_r($s2);   //  [hash] => 43232


class Test extends Single{
    public function __construct(){
    }
}

$t1 = new Test();
$t2 = new Test();
print_r($t1);   //  [hash] => 6524
print_r($t2);   //  [hash] => 4332

解决1:用final修饰父类构造函数 能继承 不能重写

  • final关键字修饰的类方法 能继承 不能重写
  • s1 s2 t1 t2 指向同一个对象
echo '<pre>';

class Single{
    public $hash;  // 随机码
    static private $instance = null;
    final private function __construct(){
        $this->hash = mt_rand(1,99999);
    }
    static public function getInstance(){
        if (! self::$instance instanceof Single){   // instanceof判断某个对象是不是某个类的实例
            self::$instance = new Single();
        }return self::$instance;
    }
}

$s1 = Single::getInstance();
$s2 = Single::getInstance();
print_r($s1);   //  [hash] => 24726
print_r($s2);   //  [hash] => 24726


class Test extends Single{
}

$t1 = Single::getInstance();
$t2 = Single::getInstance();
print_r($t1);   //  [hash] => 24726
print_r($t2);   //  [hash] => 24726

漏洞2:问题一种的漏洞解决了是吗?那我clone一下

  • $t3 = clone $t2 ,把$t2对象完全复制一份,存到一个新开辟的内存中
  • s1 s2 t1 t2 指向同一个对象,t3 指向新复制的对象
  • 这不是有2个对象了吗?
echo '<pre>';

class Single{
    public $hash;  // 随机码
    static private $instance = null;
    final private function __construct(){
        $this->hash = mt_rand(1,99999);
    }
    static public function getInstance(){
        if (! self::$instance instanceof Single){   // instanceof判断某个对象是不是某个类的实例
            self::$instance = new Single();
        }return self::$instance;
    }
}

$s1 = Single::getInstance();
$s2 = Single::getInstance();
print_r($s1);   //  [hash] => 24726
print_r($s2);   //  [hash] => 24726


class Test extends Single{
}

$t1 = Single::getInstance();
$t2 = Single::getInstance();
print_r($t1);   //  [hash] => 24726
print_r($t2);   //  [hash] => 24726

$t3 = clone $t2;
print_r($t3);   //  [hash] => 24726  这里[hash]值一样的原因是 clone $t3对象时,[hash]已经有值

if ($t3 === $t2){
    echo '是同一个对象';
}else{
    echo '不是一个对象';  // 不是一个对象
}

解决2:防止使用clone克隆对象

  • 使用魔术方法function __clone(){}防止使用clone克隆对象

单例模式最终版

类内使用self代替类名

// 单例模式最终版
class Single{
    // 创建静态私有的变量保存该类对象
    static private $instance = null;
    // 防止使用new直接创建对象
    final private function __construct(){
    }
    static public function getInstance(){
        // 判断$instance是否是Singleton的对象,不是则创建
        if (! self::$instance instanceof self){   // instanceof判断某个对象是不是某个类的实例
            self::$instance = new self();	// self代替类名更推荐
        }return self::$instance;
    }
    // 防止使用clone克隆对象  设为私有
    private function __clone(){
    }

}

$s1 = Single::getInstance();
$s2 = Single::getInstance();

单例练习

单例数据库

class DaoMysql{
    private $mysql_link;
    private static $instance = null;
    final private function __construct($host,$user,$pwd){
        $this->mysql_link = mysqli_connect($host,$user,$pwd);
    }
    public static function getInstance($host,$user,$pwd){
        if (!self::$instance inStanceof self){
            self::$instance = new self($host,$user,$pwd);
        }return self::$instance;
    }

    private function __clone(){}

}

$d1 = DaoMysql::getInstance('localhost','root','');
$d2 = DaoMysql::getInstance('localhost','root','');
var_dump($d1,$d2);

内存图分析

$instance和p1 p2指向同一个对象标识符
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值