PHP和Javascript单例模式的对比

什么是单例模式?先来看一下维基百科的解释:单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

概念理解起来可能比较困难,用最简单的话来概括单例模式就是,在创建一个类的时候,通过某种方法,来保证这个类只能产生一个实例。这么做有什么好处呢?我们知道,当我们编写好一个类之后,要产生这个类的实例是非常简单的,直接用new关键字就可以,那么,在一些大型的项目当中,同一个页面可能不是一个人开发,例如有一个Mysql的类,第一个开发人员实例化一个mysql对象,第二人去开发的时候,可能又去实例化了一个db对象,这两个对象其实都是一样的,每次实例化一个对象的时候,可能都需要去连接一次数据库,这样是非常浪费系统资源的。单例模式的出现就是为了避免对一个类的重复实例化问题的产生,从而节省资源。


首先来看一下PHP中怎么实现单例模式,我们知道,当用new实例化一个对象的时候,对象都有一个__construct的方法自动被调用,那么,我能不能把这个方法设为私有或者保护起来,从而不让new产生对象呢?来看下面这个例子:

<?php
class Human{
	static protected $obj=NULL;	//定义类的静态属性
	final protected function __construct(){
        //定义类的__construct函数为不能被子类改写的,不能被自动调用的空方法
	}
	static public function getInstance(){//定义类的静态方法,用来获取对象
		if(self::$obj instanceof self){
			return self::$obj;
		}else{
			self::$obj=new self;
			return self::$obj;
		}
	}
	final protected function __clone(){//定义类的clone方法,保证实例化的对象不能被克隆
	}
	public function say(){//公共方法
		echo "Hello";
	}
}
class Stu extends Human{
	public $name="Hito";
}
$a1=Human::getInstance();
$a2=Human::getInstance();
$a3=Stu::getInstance();
if($a1===$a2&&$a2===$a3){
echo "对象相同";
}else{
echo "对象不相同";
}
$b1=new Human();//错误:Call to protected Human::__construct() from invalid context
$b2=new Stu();//错误:Call to protected Human::__construct() from invalid context
$a4=clone $a3;//Fatal error: Call to protected Human::__clone() from context ''
$a3->name;//Undefined property: Human::$name
?>

原理是这样的:首先定义Human的一个受保护的静态属性,这个属性用来存放返回的对象(初始化为空);定义类的__construct方法为final和protected,final保证继承自Human的自对象不能改写__contruct方法,protected保证外部不能调用__construct,这样,就不能通过new来产生实例化Human的对象了;第三,定义一个静态的公共方法getInstance来获取对象,为了保证多次获取的对象都是同一个对象,把最终产生的对象赋给了Human的静态属性$obj,判断如果$obj是实例化的Human,则返回$obj,如果不是,把产生一个实例化的Human对象,然后赋值给$obj,再返回$obj。这样,就保证了多次请求返回的是同一个对象;第四,定义不能被改写的__clone魔术方法,防止实例化的对象被克隆,从而产生一个不同的对象;最后一个是共有的方法。

可以看到Stu这个类是继承于Human的,但是实际上却返回的是Human实例化的对象,也就是说我在class Stu extends Human中定义一个公共的非静态的方法或者属性,$a3是不能访问的,在上面代码的倒数第二行可以看到。
总结PHP单例模式的特点:
1,通过定义类的构造函数为私有或受保护来防止用new实例化;
2,通过final关键字保证相关属性或方法不能被子类改写;
3,通过定义魔术方法,保证对象不能被克隆
4,设置一个类的公共静态方法来产生实例化的对象,并把实例化之后的对象赋值给这个类的一个静态属性,以后每次请求产生对象,都返回这个静态属性指向的对象。

那么,在Javascript中,单例模式是什么样的呢?从上面的概念可以看出,单例模式就是为了避免类的重复实例化导致资源的浪费,实际在Javascript中,除开我们自定义的function类,其它对象字面量都是不能被实例化的,因为它们本身不是构造函数。一个简单的单例模式:

<script>
var Human={Name:"Hito",Age:25}
//或者通过函数返回对象:
var Human=(function(){
	var Name="Hito";
	var Age=25;
	return {
		Name:Name,
		Age:Age
		}
	}
)()
var xiaoming=mew Human();//Uncaught TypeError: object is not a function 
</script>

在上面的例子中,我们不能通过new来创建一个新的对象。这两种方法虽然实现了JS的单例模式,可以我并没有像PHP那样直接屏蔽掉类的new方法,我们来看最后一种实现方法:

<script>
function Human(){
    if(Human.instance){
	return Human.instance;
    }
    this.name="Hito";
    this.Age=25;
    Human.instance=this;
}
var a1=new Human();
var a2=new Human();
alert(a1===a2);//true
</script>

在这个例子中,定义一个Human类,Human类有个属性instance,如果存在的话,直接返回这个属性,如果不存在,那么则设置相应的属性。使用数据缓存来存储该单例,用作判断单例是否已经生成,是这种方法主要的实现思路。

发表回复

评论列表:

window
window
非常好的一篇文章,学习了!
回复此留言