首頁 >後端開發 >php教程 >單例模式:專為提高效率而設計

單例模式:專為提高效率而設計

PHPz
PHPz原創
2023-08-31 13:53:131017瀏覽

單例模式:專為提高效率而設計

在本文中,您將了解如何實作單例設計模式,以及為什麼以及何時在應用程式中使用此模式。正如“Singleton”這個名字所暗示的那樣,這種方法允許我們創建一個類別的唯一一個物件。

讓我們看看維基百科上有關於此設計模式的內容:

單例模式是一種將類別的實例化限制為一個物件的設計模式。當只需要一個物件來協調整個系統的操作時,這非常有用。

如上文定義中所提到的,當我們想要確保任何類別都需要建立一個且僅有一個物件時,那麼我們應該為該類別實作 Singleton 模式。

p>

#你可能會問為什麼我們應該實作這樣一個類,它允許我們只創建它的一個物件。我想說,我們可以在很多用例中應用這種設計模式。其中包括:配置類別、會話類別、資料庫類別等等。

#本文中我將以資料庫類別為例。首先,我們將看看如果沒有為這樣的類別實作單例模式,會出現什麼問題。

#問題

想像一個非常簡單的資料庫連接類,一旦我們建立該類別的對象,它就會建立與資料庫的連接。

class database {
    
	private $dbName = null, $dbHost = null, $dbPass = null, $dbUser = null;
	
	public function __construct($dbDetails = array()) {
		
		$this->dbName = $dbDetails['db_name'];
		$this->dbHost = $dbDetails['db_host'];
		$this->dbUser = $dbDetails['db_user'];
		$this->dbPass = $dbDetails['db_pass'];

		$this->dbh = new PDO('mysql:host='.$this->dbHost.';dbname='.$this->dbName, $this->dbUser, $this->dbPass);
		
	}
	
}

在上面的程式碼範例中,您可以看到,每次建立此類的物件時,它都會與資料庫建立連線。因此,如果開發人員在多個位置創建了此類的對象,想像一下它將與資料庫伺服器創建的(相同)資料庫連接的數量。

因此,開發人員在不知不覺中犯了錯誤,從而對資料庫和應用伺服器的速度產生了巨大影響。讓我們透過創建該類別的不同物件來看看同樣的事情。

$dbDetails = array(
    	'db_name' => 'designpatterns',
		'db_host' => 'localhost',
		'db_user' => 'root',
		'db_pass' => 'mysqldba'
);

$db1 = new database($dbDetails);
var_dump($db1);
$db2 = new database($dbDetails);
var_dump($db2);
$db3 = new database($dbDetails);
var_dump($db3);
$db4 = new database($dbDetails);
var_dump($db4);

// Output
object(database)[1]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[2]
object(database)[3]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[4]
object(database)[5]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[6]
object(database)[7]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[8]

如果你看到上面程式碼的輸出和輸出,你可以看到每個物件都分配了一個新的資源ID,所以所有物件都是全新的引用,因此它也分配單獨的記憶體。這樣不知不覺中我們的應用程式就會佔用實際上不需要的資源。

解決方案

開發人員如何使用我們的基礎框架不在我們的控制範圍內。程式碼審查過程發生後,它就在我們的控制之下,但在開發過程中,我們不能一直站在他們身後。

為了克服這種情況,我們應該使我們的基類不能創建一個類別的多個物件;相反,它會給出一個已經創建的物件(如果有)。在這種情況下,我們應該考慮為我們的基類開發單例模式。

在實現此模式時,我們的目標是允許一次且僅建立一個類別的物件。請允許我添加下面的類別程式碼,然後我們將詳細介紹該類別的每個部分。

class database {
    
	private $dbName = null, $dbHost = null, $dbPass = null, $dbUser = null;
	private static $instance = null;
	
	private function __construct($dbDetails = array()) {
		
		// Please note that this is Private Constructor
		
		$this->dbName = $dbDetails['db_name'];
		$this->dbHost = $dbDetails['db_host'];
		$this->dbUser = $dbDetails['db_user'];
		$this->dbPass = $dbDetails['db_pass'];

		// Your Code here to connect to database //
		$this->dbh = new PDO('mysql:host='.$this->dbHost.';dbname='.$this->dbName, $this->dbUser, $this->dbPass);
	}
	
	public static function connect($dbDetails = array()) {
		
		// Check if instance is already exists 		
		if(self::$instance == null) {
			self::$instance = new database($dbDetails);
		}
		
		return self::$instance;
		
	}
	
	private function __clone() {
		// Stopping Clonning of Object
	}
	
	private function __wakeup() {
		// Stopping unserialize of object
	}
	
}

幾乎沒有跡象表明上面的類別是 Singleton 類別。首先是私有建構函數,它防止使用 new 關鍵字建立物件。另一個指示是一個靜態成員變量,它保存對已建立物件的參考。

$dbDetails = array(
    	'db_name' => 'designpatterns',
		'db_host' => 'localhost',
		'db_user' => 'root',
		'db_pass' => 'mysqldba'
);

$db1 = database::connect($dbDetails);
var_dump($db1);
$db2 = database::connect($dbDetails);
var_dump($db2);
$db3 = database::connect($dbDetails);
var_dump($db3);
$db4 = database::connect($dbDetails);
var_dump($db4);

// Output

object(database)[1]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[2]
object(database)[1]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[2]
object(database)[1]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[2]
object(database)[1]
  private 'dbName' => string 'designpatterns' (length=14)
  private 'dbHost' => string 'localhost' (length=9)
  private 'dbPass' => string 'mysqldba' (length=8)
  private 'dbUser' => string 'root' (length=4)
  public 'dbh' => object(PDO)[2]

如果比較兩個部分的輸出,那麼您將看到,在單例模式的輸出中,所有不同物件的物件資源 ID 都是相同的。但不使用設計模式時並非如此。

單例作為反模式

由於各種原因,這種設計模式也稱為反模式,我將在下面提到:

  1. 由於其控制自身創建和生命週期的質量,它違反了單一職責原則。
  2. 它將全域狀態引入您的應用程式。我想說全域狀態非常糟糕,因為任何程式碼都可以改變它的值。所以在調試的時候很難發現哪部分程式碼做了當前階段的全域變數。

  3. 如果您正在進行單元測試,那麼單例通常是一個壞主意,並且不執行單元測試通常也是一個壞主意。

結論

我盡力解釋了網路上廣泛討論的單例設計模式。我希望這篇文章對您有幫助。我們已經介紹了該模式的兩個方面,即設計模式和反模式。

#請在下面發表您的意見、建議和/或問題,我會盡快回覆。您也可以透過 Twitter @XpertDevelopers 聯繫我或直接給我發電子郵件。

#

以上是單例模式:專為提高效率而設計的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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