搜尋
首頁php教程php手册PHP单例模式总结教程

以前我们讲过php单态设计模式之单例模式的理解及单例模式(Singleton)的常见应用场景,现在我们在原来的基础上总结一下。

单例模式,就是保持一个对象只存在一个实例,并且为该唯一实例提供一个全局访问点(一般是一个静态的getInstance方法),单例模式应用场景非常广泛,例如:

数据库操作对象、日志写入对象、全局配置解析对象等

这些场景的共同特征是从业务逻辑上来看运行期间改对象却是只需要一个实例、不断new多个实例会增加不必要的资源消耗、全局调用便利。下面分别说明这三个方面:

1.业务上只需要一个实例

以数据库连接对象为例,加入有一个购物网站,有一个MySQL数据库 127.0.0.1:3306, 那么在一个进程中无论我们需要进行多少次针对改数据库的操作,都只需要连接数据库一次,使用相同的数据库连接句柄(MySQL Connection Resource),从业务需求上来看就只需要一个实例。

相反,同样以购物网站为例,存在许多商品,这些商品都不一样(id,name,price..),这个时候需要显示一个商品列表,加入我们建立一个 Product 作为数据映射对象,那么从业务需求上来说,一个实例就无法满足业务需求,因为每个商品都不一样。

2.不断new操作增加不必要的资源消耗

我们一般会在类的构造方法(new操作肯定会调用)中进行一些业务操作,例如数据库连接对象可能会在构造方法中尝试读取数据库配置并进行数据库连接(如mysqli::__construct())、日志写入对象会判断日志写入目录是否存在并写入(不存在可能尝试创建改目录)、全局配置解析对象可能需要定位配置文件的保存目录并进行文件扫描等。

这些业务都会消耗相当的资源,如果在一个进程中我们值需要做一次,将会非常有利于我们提高应用的运行效率。

3. 全局调用便利

因为单例模式的一大特点就是通过静态方法获取对象实例,那么就意味着访问对象的方法时不需要先new一个对象的实例,如果改对象需要很多地方使用,则提高了调用的便利性。

通过一个日志操作类来举例,代码如下:

class Logger{ 
 
	//首先,需要一个私有的静态变量来存储产生的对象实例 
	private static $instance; 
 
	//业务变量,保存日志写入路径 
	private $logDir; 
 
	//构造方法,注意必须也是私有的,不允许被外部实例化(即在外部被new) 
	private function __construct(){ 
		//调试输出,测试对象被new的次数 
		echo "new Logger instance rn"; 
		$logDir = sys_get_temp_dir(). DIRECTORY_SEPARATOR . "logs"; 
		if(!is_dir($logDir) || !file_exists($logDir)){ 
			@mkdir($logDir); 
		} 
		$this->logDir = $logDir; 
 
	} 
 
	//类唯一实例的全局访问点,用于判断并返回对象实例,供外部调用 
	public static function getInstance(){ 
		if(is_null(self::$instance)){ 
			$class = __CLASS__; //获取本对象的类型,也可以用new self()方式 
			self::$instance = new $class(); 
		} 
		return self::$instance; 
	} 
 
	//重载__clone()方法,不允许对象对克隆 
	public function __clone(){ 
		throw new Exception("Singleton Class Can Not Be Cloned"); 
	} 
 
	//具体的业务方法,实际可以有很多方法 
	public function logError($message){ 
		$logFile = $this->logDir . DIRECTORY_SEPARATOR . "error.log"; 
		error_log($message, 3, $logFile); 
	}  
} 
 
//日志调用 
$logger = Logger::getInstance(); 
$logger->logError("An error occured"); 
$logger->logError("Another error occured"); 
 
//或者这样调用 
Logger::getInstance()->logError("Still have error"); 
Logger::getInstance()->logError("I should fix it");

在单例模式中可能遇到一种比较特殊的情况,比如数据库连接对象,对于大型应用来说,很可能需要连接多台数据库,那么不同的数据库公用一个对象可能会产生问题,比如连接的分配、获取insert_id,last_error等。这个问题也比较好解决,就是把我们的$instance变量变成一个关联数组,通过给getInstance方法传入不同的参数获取不同的"单例对象"(引号的含义是:严格来说类可能被new多次,但是这个new也是在我们的控制之内的,而不是在类外部),代码如下:

class MysqlServer{ 
	//注意,变成复数了哦^_^  当然只是为了标识而已 
	private static $instances = array(); 
 
	//业务变量,保持当前实例的mysqli对象 
	private $conn; 
 
	//显著特征:私有的构造方法,避免在类外部被实例化 
	private function __construct($host, $username, $password, $dbname, $port){ 
		$this->conn = new mysqli($host, $username, $password, $dbname, $port); 
	} 
 
	//类唯一实例的全局访问点 
	public static function getInstance($host='localhost', $username='root', $password='123456', $dbname='mydb', $port='3306'){ 
		$key = "{$host}:{$port}:{$username}:{$dbname}"; 
		if (emptyempty(self::$instances[$key])){ 
			//这里也可以用 new self(); 的方式 
			$class = __CLASS__; 
			self::$instances[$key] = new $class($host, $username, $password, $dbname, $port); 
		} 
		return self::$instances[$key]; 
	} 
 
	//重载__clone方法,不允许对象实例被克隆 
	public function __clone(){ 
		throw new Exception("Singleton Class Can Not Be Cloned"); 
	} 
 
	//查询业务方法,后面省略其它业务方法 
	public function query($sql){ 
		return $this->conn->query($sql); 
	} 
 
	//尽早释放资源 
	public function __destruct(){ 
		$this->conn->close(); 
	} 
}

问题1:单例类能否拥有子类,因为单例类的构造方法是私有的,因此无法被继承,如果要继承则需要将构造方法改为protected或public,这就违背了单例模式的本意。因此,如果你想给单例类加子类,那就需要回头想想是否错用了模式,或者结构设计上有问题。

问题2:单例滥用,单例模式相对来说比较好理解和实现,因此一旦认识到单例模式的好处,很可能什么类都想写成单例,因此在使用次模式之前一定要考虑上述3种情况,看是否真的有必要使用。


教程网址:

欢迎收藏∩_∩但请保留本文链接。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器