【PHP设计模式】创建型之单例(Singleton)

单例模式

意图:

  【GoF】保证一个类仅有一个实例,并提供一个访问它的全局访问点。

动机:

  我们有时候需要提供一个类,我们怎样才能保证一个类只有一个实例并且这个实例易于被访问?一个全局对象使得一个对象可以被访问,但是它不能防止你实例化多个实例。你的任何代码都能修改全局变量,这将不可避免的引起更多调试的意外。换句话说,全局变量的状态总是会出现一些问题的。一个更好的办法就是让类自身负责保存它的实例。这个类可以保证没有其他实例被创建(通过截取创建新对象的请求),并且提供一个访问这个实例的方法。这个实现过程就是单例(Singleton)模式。

适用:

  一、当类只能有一个实例而且可以可以从一个众所周知的访问点访问它时。

  二、当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能适用一个扩展的实例时。

类图:

Singleton-结构

  如上图所示:参与者Singleton定义了一个Instance()操作,允许客户访问它的唯一实例。同时负责创建它的唯一实例。

交互图:

singleton调用时序

如上图所示:客户通过调用Singleton类的Instance操作访问一个Singleton的实例,从而可以执行这个实例对象的其他操作。

在PHP中:

  1.必须拥有一个构造函数,并且被标记为private。这是为了实现动机中的“通过截取创建新对象的请求”,从而避免创建多个实例,只拥有一个唯一实例。

  2.拥有一个保存类的实例的静态成员变量。这个成员变量在实现延迟实例化的时候是必要的。

  3.拥有一个访问这个实例的公共的静态方法。这就是上面的Instance操作的方法,它必须是静态的public的。

 

示例代码一:

class Preferences{
  private $props = array();
  private static $instance;//用于存放生成的对象,外部不能访问。

  private function __construct(){}//初始化代码
  
  public static getInstance(){//public的静态方法,可以在外部调用
    if(empty(slef::$instance)){//静态属性只能被类调用,而不能被对象调用。静态方法只能访问静态属性,不能访问其他属性
      self::$instance = new Preferences();
    }
    return self::$instance;
  }
  
  public function setProperty($key,$value){
    $this->props[$key] = $value;
  }
  public function getProperty($key){
    $this->props[$key];
  }
}

  静态方法不能访问普通属性,但是静态方法可以访问静态属性。如果要让单例类起作用,就避免使其为其他类提供一个实例,用它调用各种方法。单例类不会创建实例副本,而是将类内部存储的实例返回。单例就不会占用内存和系统资源了。

__clone():这里还有漏洞,就是可以复制对象,破坏单一职责!

  在PHP4中,$first和$second是两个完全不同的对象;

  在PHP5中,$first和$second指向同一个对象。

  为了防止对象被赋值或者克隆,创建了一个空的私有方法__clone()方法。

class CopyMe{}
$first = new CopyMe();

$second = $first;

  如果我们想得到$first的副本,那么可以用clone关键字。

class CopyMe{}
$first = new CopyMe();

$second = clone $first;//此时的$second和$first是不同的两个对象了

  如果对这两个不同的对象进行操作,比如说更新数据库内容,这样有可能两个对象对应表中的同一条字段,所以可以在类中默认克隆这个对象的时候执行一些默认的操作,这就用到了__clone()方法了!

class Person{
  private $name;
  private $age;
  private $id;

  function __construct($name,$age){
    $this->name = $name;
    $this->age= $age;
  }

  function setId($id){
    $this->id = $id;
  }
  //在执行对象复制的时候,产生了新的副本,这个副本的__clone()方法会执行
  function __clone(){
    $this->id = 0;//在克隆的副本中,这个id会被设置为0
  }
}

示例代码二:

  返回引用的方法通常被命名为getInstance()。这个方法需要是静态的,使用instanceof操作符合self关键字检测类是否实例化。如果没有实例化就必须实例化。如:

class Database{
  private $_db;//实例对象,构造对象时这个变量被填充
  static $_instance;//这个变量会保存仅有的一个实例

  //防止外部代码使用new来创建对象
  private function __construct(){
    $this->_db = pg_connect('dbname=example_db');
  }

  private __clone(){};//消除可以复制对象破坏单一职责的漏洞

  public static function getInstance(){
    //检查静态实例变量是否已经保存这个类的一个实例
    if (! self::$_instance instanceof self)){
      self::$_instance = new self();
    }
    return self::$_instance;
  }

  public function query($sql){
    return pg_query($this->_db,$sql);
  }
}

示例代码三:

class RequestContext {
  
  //...具体的方法

  /**  *以下实现singleton*/
    private static $context;//定义存放实例的私有静态属性
    private function __construct(){//私有构造函数
        $this->from_index=defined('RUN_FROM_INDEX') && RUN_FROM_INDEX;
        if(self::$context) throw new Exception("RequestContext CANNOT create! use 'instance' method");
    }
    public static function instance(){//公用的静态方法返回唯一实例
        if(! isset(self::$context))    self::$context=new RequestContext();
        return self::$context;//单例生成全局对象
    }
}

   单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

原文地址:https://www.cnblogs.com/colorstory/p/2644213.html