首先,我定義個類,實現單例模式:(這裡是簡單一寫,就是個最基本的單例)
class Demo{ public static $instance; private function __Construct() { //TODO } public static function getInstance() { if(!self::$instance){ self::$instance = new static(); } return self::$instance; } public function call() { //其他方法 }}
下面有兩種方式執行個體化類:
1.在需要用的地方
$aa = Demo::getInstance();$bb = Demo::getInstance();$cc = Demo::getInstance();
這樣調用肯定是沒問題的,一般情況也是這樣初始化。
2.定義一個普通類,寫個函數初始化,儲存在一個靜態變數,如下:
類:
class Demo{ public function __Construct(){ //TODO }}
函數:
function get_obj(){ static $obj; if($obj){ return $obj; }else{ $obj = new Demo; return $obj; }}
在需要調用的地方這樣寫:
$obj = get_obj();$obj->call();$obj2 = get_obj();$obj2->call();$obj3 = get_obj();$obj3 = get_obj();
這樣不是也只執行個體化一次這個類嗎?
回複內容:
首先,我定義個類,實現單例模式:(這裡是簡單一寫,就是個最基本的單例)
class Demo{ public static $instance; private function __Construct() { //TODO } public static function getInstance() { if(!self::$instance){ self::$instance = new static(); } return self::$instance; } public function call() { //其他方法 }}
下面有兩種方式執行個體化類:
1.在需要用的地方
$aa = Demo::getInstance();$bb = Demo::getInstance();$cc = Demo::getInstance();
這樣調用肯定是沒問題的,一般情況也是這樣初始化。
2.定義一個普通類,寫個函數初始化,儲存在一個靜態變數,如下:
類:
class Demo{ public function __Construct(){ //TODO }}
函數:
function get_obj(){ static $obj; if($obj){ return $obj; }else{ $obj = new Demo; return $obj; }}
在需要調用的地方這樣寫:
$obj = get_obj();$obj->call();$obj2 = get_obj();$obj2->call();$obj3 = get_obj();$obj3 = get_obj();
這樣不是也只執行個體化一次這個類嗎?
完善的單例應該是這樣的
class Foobar { static private $instance; // 禁止外部new Foobar private function __construct() { } // 禁止clone $foobar private function __clone() { } static public function getInstance() { retrun self::$instance ?: (self::$instance = new self); }}
如果還要考慮到繼承的話
class Foo { static private $instances = []; protected function __construct() { } final private function __clone() { } final static public function getInstance() { $class = get_called_class(); if (!isset(self::$instances[$class])) { self::$instances[$class] = new static; } return self::$instances[$class]; }}class Bar extends Foo {}$foo = Foo::getInstance();$bar = Bar::getInstance();
get_obj()那種寫法,也可以達到目的,但無法禁止new和clone,也就無法做到真正的單例
這問題我也有印象,我也答過一次
http://segmentfault.com/q/1010000003894638
看了一下還是題主你,為毛要問兩遍……
然後你的第二種方式並非單例,請不要陷入摳概念的誤區
偶爾也有你這種用法的,少見,並且不好。在一般情況下用一個特定對象就行了,特殊情況下需要新的執行個體的場合會用,但一般也不會這麼寫(早期的PHP會有一些類這麼做)
= = 所以呢?
兩種寫法而已啊。。。
另外,你無論怎麼看,都還是第一種更清晰,更獨立,更好用啊。。。
第一種封裝、抽象的更好,更加符合物件導向啊。
存一個靜態變數或者另外弄個函數什麼的,你不覺得亂嘛。。。
靜態變數可以定義在函數裡或者類屬性,兩種寫法都可以,但前者封裝性會比較好些。
樓主給了我啟發,過程式編程中,在全域函數內用靜態變數儲存資料庫連接實現單例:
'127.0.0.1', 'db_username' => 'root', 'db_password' => '', 'db_name' => 'mysql');// functions.phpfunction cn_db() { global $app; static $mysqli; if ($mysqli) { return $mysqli; } else { $mysqli = new mysqli($app['db_host'], $app['db_username'], $app['db_password'], $app['db_name']); return $mysqli; }}function cn_foo1() { $mysqli = cn_db(); return $mysqli->query('select user,host from user where user = \'root\'')->fetch_all();}function cn_foo2() { $mysqli = cn_db(); return $mysqli->query('select user,host from user')->fetch_all();}// controller + viewprint_r(cn_foo1());print_r(cn_foo2());
$instance = new static(); (樓主這行代碼寫錯了),感覺單例模式就是個思想,你第一個例子就是物件導向,第二個例子既有物件導向,又有面向過程。
單例模式是指整個應用中類只有一個對象執行個體的設計模式。
第二種方式是單例嗎?
class Demo { public function __construct(){ //TODO }}function get_obj() { static $obj; if($obj){ return $obj; }else{ $obj = new Demo; return $obj; }}var_dump(get_obj());var_dump(new Demo);
得出的是2個不同的執行個體
你那樣寫不符合物件導向思想的,而且你的單例模式也不完整,沒有考慮到clone和extends的情況。可以參考下http://www.xtwind.com/design-pattern-singleton.html
單例模式,是一種設計思想實現方式
單例首先是物件導向裡面的概念,
第二種就不是物件導向
get_obj()那種寫法無法保證外部不new Demo對象
單例的要義第一:私人化構造方法,當然PHP還提供了clone魔術方法,也要私人化。這樣做事為了保證外部執行個體化對象的入口只有一個,只能是我們暴露出來的靜態函數getInstance()
單例模式,三私一公,其中的三私:構造方法,複製魔術方法,執行個體化對象。一公:對外提供的方法