這篇文章主要介紹了PHP中常用的三種設計模式,結合實例形式詳細分析了php單例模式、工廠模式與觀察者模式概念、功能、相關使用技巧與操作注意事項,需要的朋友可以參考下
本文實例講述了PHP中常用的三種設計模式。分享給大家供大家參考,具體如下:
PHP中常用的三種設計模式:單例模式、工廠模式、觀察者模式
1.單例模式
爲何要使用PHP單例模式?
多數人都是從單例模式的字面上的意思來理解它的用途, 認爲這是對系統資源的節省, 可以避免重複實例化, 是一種”計劃生育”. 而PHP每次執行完頁面都是會從內存中清理掉所有的資源. 因而PHP中的單例實際每次運行都是需要重新實例化的, 這樣就失去了單例重複實例化的意義了. 單單從這個方面來說, PHP的單例的確有點讓各位失望. 但是單例僅僅只有這個功能和應用嗎? 答案是否定的。
- php的應用主要在於數據庫應用, 所以一個應用中會存在大量的數據庫操作, 在使用面向對象的方式開發時(廢話), 如果使用單例模式, 則可以避免大量的new 操作消耗的資源。
- 如果系統中需要有一個類來全局控制某些配置信息, 那麼使用單例模式可以很方便的實現. 這個可以參看zend Framework的FrontController部分。
- 在一次頁面請求中, 便於進行調試, 因爲所有的代碼(例如數據庫操作類db)都集中在一個類中, 我們可以在類中設置鉤子, 輸出日誌,從而避免到處var_dump, echo。
一個單例類應包括以下幾點:
和普通類不同,單例類不能被直接實例化,只能是由自身實例化。因此,要獲得這樣的限制效果,構造函數必須標記爲private。
要讓單例類不被直接實例化而能起到作用,就必須爲其提供這樣的一個實例。因此,就必須要讓單例類擁有一個能保存類的實例的私有靜態成員變量和對應的一個能訪問到實例的公共靜態方法。
在PHP中,爲防止對單例類對象的克隆來打破單例類的上述實現形式,通常還爲其提供一個空的私有__clone()
方法。
對於一個類的對象,如果使用“clone運算符”,就會複製出一個和當前對象完全一樣的新對象出來,並且,此時還會自動調用該類的魔術方法:__clone()
(只要該類中有該方法)。
則要實現單例類,就應該對這個單例類的對象“禁止克隆”,用private來修飾__clone()來實現禁止克隆,具體可參考單例類的加強:禁止克隆。
單例模式的例子:
<?php /** * 設計模式之單例模式 * $instance必須聲明爲靜態的私有變量 * 構造函數和析構函數必須聲明爲私有,防止外部程序new * 類從而失去單例模式的意義 * getInstance()方法必須設置爲公有的,必須調用此方法 * 以返回實例的一個引用 * ::操作符只能訪問靜態變量和靜態函數 * new對象都會消耗內存 * 使用場景:最常用的地方是數據庫連接。 * 使用單例模式生成一個對象後, * 該對象可以被其它衆多對象所使用。 */ class SingetonBasic { private static $instance; //靜態變量要私有化,防止類外修改 // other vars.. private function __construct() { //構造函數私有化,類外不能直接新建對象 // do construct.. } private function __clone() {} //在__clone()前用private修飾,用來禁止克隆 public static function getInstance() { //公共的靜態方法,public——外部的接口,static——不使用對象而是通過類名訪問 if (!(self::$instance instanceof self)) { //私有靜態變量$instance爲空 self::$instance = new self(); //新建爲自身的對象,並賦值給私有變量$instance } return self::$instance; //返回私有變量$instance } // other functions.. } $a = SingetonBasic::getInstance(); $b = SingetonBasic::getInstance(); var_dump($a === $b); //結果爲:boolean true //?>
<?php /** * php單例,單例模式爲何只能實例化一次 */ class Example{ // 保存類實例在此屬性中 private static $instance; // 構造方法聲明爲private,防止直接創建對象 private function __construct(){ echo 'I am constructed'; } // singleton 方法 public static function singleton(){ if (!isset(self::$instance)) {//判斷是否以前創建了當前類的實例 $c = __CLASS__;//獲取類名 self::$instance = new $c;//如果沒有創建,實例化當前類,這裏實現類只實例化一次 } return self::$instance;//返回類的實例 } // Example類中的普通方法 public function bark(){ echo 'Woof!'; } // 阻止用戶複製對象實例 public function __clone(){ trigger_error('Clone is not allowed.', E_USER_ERROR); } } // 這個寫法會出錯,因爲構造方法被聲明爲private $test = new Example; // 下面將得到Example類的單例對象 $test = Example::singleton(); $test->bark(); // 下面將得到Example類的單例對象 $test = Example::singleton(); $test->bark(); // 複製對象將導致一個E_USER_ERROR. $test_clone = clone $test; ?>
關於__clone()
方法可參考: PHP對象克隆__clone()介紹
2. 工廠模式
工廠模式在於可以根據輸入參數或者應用程序配置的不同來創建一種專門用來實現化並返回其它類的實例的類。
工廠模式的例子:
<?php class FactoryBasic { public static function create($config) { } }
比如這裏是一個描述形狀對象的工廠,它希望根據傳入的參數個數不同來創建不同的形狀。
<?php // 定義形狀的公共功能:獲取周長和麪積。 interface IShape { function getCircum(); function getArea(); } // 定義矩形類 class Rectangle implements IShape { private $width, $height; public function __construct($width, $height) { $this->width = $width; $this->height = $height; } public function getCircum() { return 2 * ($this->width + $this->height); } public function getArea() { return $this->width * $this->height; } } // 定義圓類 class Circle implements IShape { private $radii; public function __construct($radii) { $this->radii = $radii; } public function getCircum() { return 2 * M_PI * $this->radii; } public function getArea() { return M_PI * pow($this->radii, 2); } } // 根據傳入的參數個數不同來創建不同的形狀。 class FactoryShape { public static function create() { switch (func_num_args()) { case 1: return new Circle(func_get_arg(0)); break; case 2: return new Rectangle(func_get_arg(0), func_get_arg(1)); break; } } } // 矩形對象 $c = FactoryShape::create(4, 2); var_dump($c->getArea()); // 圓對象 $o = FactoryShape::create(2); var_dump($o->getArea());
使用工廠模式使得在調用方法時變得更容易,因爲它只有一個類和一個方法,若沒有使用工廠模式,則要在調用時決定應該調用哪個類和哪個方法;使用工廠模式還使得未來對應用程序做改變時更加容易,比如要增加一種形狀的支持,只需要修改工廠類中的create()一個方法,而沒有使用工廠模式,則要修改調用形狀的代碼塊。
3. 觀察者模式
觀察者模式爲您提供了避免組件之間緊密耦合的另一種方法。該模式非常簡單:一個對象通過添加一個方法(該方法允許另一個對象,即觀察者註冊自己)使本身變得可觀察。當可觀察的對象更改時,它會將消息發送到已註冊的觀察者。這些觀察者使用該信息執行的操作與可觀察的對象無關。結果是對象可以相互對話,而不必瞭解原因。
一個簡單的示例:當聽衆在收聽電臺時(即電臺加入一個新聽衆),它將發送出一條提示消息,通過發送消息的日誌觀察者可以觀察這些消息。
<?php // 觀察者接口 interface IObserver { function onListen($sender, $args); function getName(); } // 可被觀察接口 interface IObservable { function addObserver($observer); function removeObserver($observer_name); } // 觀察者類 abstract class Observer implements IObserver { protected $name; public function getName() { return $this->name; } } // 可被觀察類 class Observable implements IObservable { protected $observers = array(); public function addObserver($observer) { if (!($observer instanceof IObserver)) { return; } $this->observers[] = $observer; } public function removeObserver($observer_name) { foreach ($this->observers as $index => $observer) { if ($observer->getName() === $observer_name) { array_splice($this->observers, $index, 1); return; } } } } // 模擬一個可以被觀察的類:RadioStation class RadioStation extends Observable { public function addListener($listener) { foreach ($this->observers as $observer) { $observer->onListen($this, $listener); } } } // 模擬一個觀察者類 class RadioStationLogger extends Observer { protected $name = 'logger'; public function onListen($sender, $args) { echo $args, ' join the radiostation.<br/>'; } } // 模擬另外一個觀察者類 class OtherObserver extends Observer { protected $name = 'other'; public function onListen($sender, $args) { echo 'other observer..<br/>'; } } $rs = new RadioStation(); // 注入觀察者 $rs->addObserver(new RadioStationLogger()); $rs->addObserver(new OtherObserver()); // 移除觀察者 $rs->removeObserver('other'); // 可以看到觀察到的信息 $rs->addListener('cctv'); ?>
更多關於PHP相關內容感興趣的讀者可查看本站專題:《php面向對象程序設計入門教程》、《PHP數組(Array)操作技巧大全》、《PHP基本語法入門教程》、《PHP運算與運算符用法總結》、《php字符串(string)用法總結》、《php+mysql數據庫操作入門教程》及《php常見數據庫操作技巧彙總》
希望本文所述對大家PHP程序設計有所幫助。