PHP单例 Pattern: 一步一步的解决问题

分享于 

11分钟阅读

Web开发

  繁體 雙語

介绍

设计模式是软件开发中常见问题的良好测试解决方案。 在著名的四本书中,23个设计模式被创建,它们被广泛应用于面向对象编程。比如 Java和 C#。 PHP 5.0是完全面向对象语言,并且在PHP中也广泛使用了一些设计模式,并且很多MVC框架也使用了它。 现在,让我们使用一步一步的和问题解决方法来实现PHP中最常见的单例 Pattern。

方案

在PHP开发( 尤其是使用MVC框架) 中,我们经常需要包含/需要一个DB类,一个上传类或者Cookie类,如下所示:


<?php



require 'DB.class.php';



require 'Upload.class.php';



require 'Cookie.class.php';



但是我们需要确保只有一个数据库或者Cookie类的实例为1 ,因为一个实例已经足够了,而且会有更多的问题。 这是在 Pattern 进入图片的时候。 简单地说,单例 Pattern 意味着如果程序员已经实例化了某个类的对象,另一个程序员不能实例化。 现在让我们来看看如何从头开始。

实现

步骤:创建一个公共类。

我们只创建一个名为Singleton的类,现在我们可以实例化任意数量的对象。 在这里,我们可以使用类Singleton创建两个对象:


class Singleton {



}



$s1 = new Singleton();



$s2 = new Singleton();



是 $s1 和 $s2 是相同的对象? 当然,我们可以通过使用简单的if() 语句来判断:


class Singleton {



}



$s1 = new Singleton();



$s2 = new Singleton();



if ($s1 === $s2) {



 echo 's1 and s2 are the same object';



} else {



 echo 's1 and s2 are NOT the same object';



}



注意:判断两个对象是否相同时,必须使用"===",而非"=="。

步骤2: 禁止"新建"操作

如果我们想阻止类用户创建他们想要的任意数量的单个类对象,那么如何执行? 你可能会认为对象是由类的构造函数创建的。 如何隐藏类本身内的构造函数,以便外部不能使用它,如下面的代码所示:


class Singleton2 {



 protected function __construct() {



 }



}



$s3 = new Singleton2(); 


//Fatal error: Call to protected Singleton2::__construct() from invalid context



现在你可以看到构造函数 protected,但是这个新问题是: 没有人可以创建对象。

步骤3: 创建对象创建方法

我们需要将对象创建为'接口',因此我们需要添加一个名为 getIns ( )的对象,并添加一个名为//的新方法。


class Singleton3 {



 public static function getIns() {



 return new self();



 }



 protected function __construct() {



 }



}



我们只在类中创建一个方法。 在 getIns ( ) 中,我们创建了类本身的对象,然后返回它。 现在我们再次测试:


class Singleton3 {



 public static function getIns() {



 return new self();



 }



 protected function __construct() {



 }



}



$s4 = Singleton3::getIns();



$s5 = Singleton3::getIns();



if ($s4 === $s5) {



 echo 's4 and s5 are the same object';



} else {



 echo 's4 and s5 are NOT the same object';



}



结果是:





s4 and s5 are NOT the same object



这两个对象仍然不是同一个对象,为什么? 因为 Singleton3::getIns() 被调用两次,因此创建了两个对象。 但现在,控制是在我们的手中,我们可以改进 getIns(),让它做我们想要的。

步骤4: 改进 getIns() 方法

现在我们可以在 getIns() 中添加检查了:


class Singleton4 {



 protected static $ins = NULL;



 public static function getIns() {



  if (self::$ins === null) {



 self::$ins = new self();



 }



 return self::$ins;





 }



 protected function __construct() {



 }



}



$s6 = Singleton4::getIns();



$s7 = Singleton4::getIns();



if ($s6 === $s7) {



 echo 's6 and s7 are the same object';



} else {



 echo 's6 and s7 are NOT the same object';



}



现在可以看到,我们将类的实例存储在一个名为 $ins.的protected 属性中 当调用 getIns() 时,我们将进行一些检查: 如果 $ins 为空,那么我们将实例化类对象。 最后,我们返回自己:: $ins。

我们来测试一下:





s6 and s7 are the same object



现在我们得到了我们想要的结果 ! 现在只有一个类的实例。 但现在还是足够了? 如果有另一个名为Multi的类,它继承了我们的原始类:


class Singleton4 {



 protected static $ins = NULL;



 public static function getIns() {



 if (self::$ins === null) {



 self::$ins = new self();



 }



 return self::$ins;



 }



 protected function __construct() {



 }



}



class Multi extends Singleton4 {



 public function __construct() {



 }



}



$s6 = Singleton4::getIns();



$s7 = Singleton4::getIns();



if ($s6 === $s7) {



 echo 's6 and s7 are the same object';



} else {



 echo 's6 and s7 are NOT the same object';



}



echo '<br>';



$s8 = new Multi();



$s9 = new Multi();



if ($s8 === $s9) {



 echo 's8 and s9 are the same object';



} else {



 echo 's8 and s9 are NOT the same object';



}



在这里子类中,父构造函数的可见性更改为 public,现在发生了什么:





s6 and s7 are the same object


s8 and s9 are NOT the same object



子类中的这一行代码:


public function __construct() {



}




销毁了我们目前所做的一切 ! 因此我们需要防止子类改变父类构造函数的可见性。

步骤:防止子类重写父类构造函数

现在,我们需要在父构造函数前面添加关键字最后:


class Singleton5 {



 protected static $ins = NULL;



 public static function getIns() {



 if (self::$ins === null) {



 self::$ins = new self();



 }



 return self::$ins;



 }



 // Adding final, so this method cannot be override!



 final protected function __construct() {



 }



}



现在,如果我们再次运行脚本:


class Singleton5 {



 protected static $ins = NULL;



 public static function getIns() {



 if (self::$ins === null) {



 self::$ins = new self();



 }



 return self::$ins;



 }



 // Adding final, so this method cannot be override!



 final protected function __construct() {



 }



}



我们可以看到输出:


Fatal error: Cannot override final method Singleton5::__construct()



现在完成了还没有。如果有密码:?


class Singleton5 {



 protected static $ins = NULL;



 public static function getIns() {



 if (self::$ins === null) {



 self::$ins = new self();



 }



 return self::$ins;



 }



 // Adding final, so this method cannot be override!



 final protected function __construct() {



 }



}



$s10 = Singleton5::getIns();



$s11 = clone $s10; // s11 cloned s10, then more than one object is created!



if ($s10 === $s11) {



 echo 's10 and s11 are the same object';



} else {



 echo 's10 and s11 are NOT the same object';



}



结果:





s10 and s11 are NOT the same object



这是因为这里使用了的克隆关键字。 所以问题又发生了。 如何防止克隆?

步骤六:禁止 __clone() 魔术函数


class Singleton6 {



 protected static $ins = NULL;



 public static function getIns() {



 if (self::$ins === null) {



 self::$ins = new self();



 }



 return self::$ins;



 }



 // Adding final, so this method cannot be override!



 final protected function __construct() {



 }



 // Forbid clone



 final protected function __clone(){



 }



}



$s10 = Singleton6::getIns();



$s11 = clone $s10; // Fatal error: Call to protected Singleton6::__clone()



现在克隆是不可能的。

最后,我们在PHP中完成了单例 !

我希望这一步逐步地实现单一的方法对你有帮助。 了解设计 Pattern 是很好的,但我想如果我们知道为什么需要创建这样的Pattern,我们的目标更为重要。


IMP  PHP  Implementation  SIN  pattern  STEP  
相关文章