PHP中常用的三種設(shè)計(jì)模式詳解【單例模式、工廠模式、觀察者模式】
本文實(shí)例講述了PHP中常用的三種設(shè)計(jì)模式。分享給大家供大家參考,具體如下:
PHP中常用的三種設(shè)計(jì)模式:?jiǎn)卫J健⒐S模式、觀察者模式
1.單例模式
為何要使用PHP單例模式?
多數(shù)人都是從單例模式的字面上的意思來理解它的用途, 認(rèn)為這是對(duì)系統(tǒng)資源的節(jié)省, 可以避免重復(fù)實(shí)例化, 是一種”計(jì)劃生育”. 而PHP每次執(zhí)行完頁面都是會(huì)從內(nèi)存中清理掉所有的資源. 因而PHP中的單例實(shí)際每次運(yùn)行都是需要重新實(shí)例化的, 這樣就失去了單例重復(fù)實(shí)例化的意義了. 單單從這個(gè)方面來說, PHP的單例的確有點(diǎn)讓各位失望. 但是單例僅僅只有這個(gè)功能和應(yīng)用嗎? 答案是否定的。
- php的應(yīng)用主要在于數(shù)據(jù)庫應(yīng)用, 所以一個(gè)應(yīng)用中會(huì)存在大量的數(shù)據(jù)庫操作, 在使用面向?qū)ο蟮姆绞介_發(fā)時(shí)(廢話), 如果使用單例模式, 則可以避免大量的new 操作消耗的資源。
- 如果系統(tǒng)中需要有一個(gè)類來全局控制某些配置信息, 那么使用單例模式可以很方便的實(shí)現(xiàn). 這個(gè)可以參看zend Framework的FrontController部分。
- 在一次頁面請(qǐng)求中, 便于進(jìn)行調(diào)試, 因?yàn)樗械拇a(例如數(shù)據(jù)庫操作類db)都集中在一個(gè)類中, 我們可以在類中設(shè)置鉤子, 輸出日志,從而避免到處var_dump, echo。
一個(gè)單例類應(yīng)包括以下幾點(diǎn):
和普通類不同,單例類不能被直接實(shí)例化,只能是由自身實(shí)例化。因此,要獲得這樣的限制效果,構(gòu)造函數(shù)必須標(biāo)記為private。
要讓單例類不被直接實(shí)例化而能起到作用,就必須為其提供這樣的一個(gè)實(shí)例。因此,就必須要讓單例類擁有一個(gè)能保存類的實(shí)例的私有靜態(tài)成員變量和對(duì)應(yīng)的一個(gè)能訪問到實(shí)例的公共靜態(tài)方法。
在PHP中,為防止對(duì)單例類對(duì)象的克隆來打破單例類的上述實(shí)現(xiàn)形式,通常還為其提供一個(gè)空的私有__clone()方法。
對(duì)于一個(gè)類的對(duì)象,如果使用“clone運(yùn)算符”,就會(huì)復(fù)制出一個(gè)和當(dāng)前對(duì)象完全一樣的新對(duì)象出來,并且,此時(shí)還會(huì)自動(dòng)調(diào)用該類的魔術(shù)方法:__clone()(只要該類中有該方法)。
則要實(shí)現(xiàn)單例類,就應(yīng)該對(duì)這個(gè)單例類的對(duì)象“禁止克隆”,用private來修飾__clone()來實(shí)現(xiàn)禁止克隆,具體可參考單例類的加強(qiáng):禁止克隆。
單例模式的例子:
<?php
/**
* 設(shè)計(jì)模式之單例模式
* $instance必須聲明為靜態(tài)的私有變量
* 構(gòu)造函數(shù)和析構(gòu)函數(shù)必須聲明為私有,防止外部程序new
* 類從而失去單例模式的意義
* getInstance()方法必須設(shè)置為公有的,必須調(diào)用此方法
* 以返回實(shí)例的一個(gè)引用
* ::操作符只能訪問靜態(tài)變量和靜態(tài)函數(shù)
* new對(duì)象都會(huì)消耗內(nèi)存
* 使用場(chǎng)景:最常用的地方是數(shù)據(jù)庫連接。
* 使用單例模式生成一個(gè)對(duì)象后,
* 該對(duì)象可以被其它眾多對(duì)象所使用。
*/
class SingetonBasic {
private static $instance; //靜態(tài)變量要私有化,防止類外修改
// other vars..
private function __construct() { //構(gòu)造函數(shù)私有化,類外不能直接新建對(duì)象
// do construct..
}
private function __clone() {} //在__clone()前用private修飾,用來禁止克隆
public static function getInstance() { //公共的靜態(tài)方法,public——外部的接口,static——不使用對(duì)象而是通過類名訪問
if (!(self::$instance instanceof self)) { //私有靜態(tài)變量$instance為空
self::$instance = new self(); //新建為自身的對(duì)象,并賦值給私有變量$instance
}
return self::$instance; //返回私有變量$instance
}
// other functions..
}
$a = SingetonBasic::getInstance();
$b = SingetonBasic::getInstance();
var_dump($a === $b); //結(jié)果為:boolean true
//?>
<?php
/**
* php單例,單例模式為何只能實(shí)例化一次
*/
class Example{
// 保存類實(shí)例在此屬性中
private static $instance;
// 構(gòu)造方法聲明為private,防止直接創(chuàng)建對(duì)象
private function __construct(){
echo 'I am constructed';
}
// singleton 方法
public static function singleton(){
if (!isset(self::$instance)) {//判斷是否以前創(chuàng)建了當(dāng)前類的實(shí)例
$c = __CLASS__;//獲取類名
self::$instance = new $c;//如果沒有創(chuàng)建,實(shí)例化當(dāng)前類,這里實(shí)現(xiàn)類只實(shí)例化一次
}
return self::$instance;//返回類的實(shí)例
}
// Example類中的普通方法
public function bark(){
echo 'Woof!';
}
// 阻止用戶復(fù)制對(duì)象實(shí)例
public function __clone(){
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
}
// 這個(gè)寫法會(huì)出錯(cuò),因?yàn)闃?gòu)造方法被聲明為private
$test = new Example;
// 下面將得到Example類的單例對(duì)象
$test = Example::singleton();
$test->bark();
// 下面將得到Example類的單例對(duì)象
$test = Example::singleton();
$test->bark();
// 復(fù)制對(duì)象將導(dǎo)致一個(gè)E_USER_ERROR.
$test_clone = clone $test;
?>
關(guān)于__clone()方法可參考: PHP對(duì)象克隆__clone()介紹
2. 工廠模式
工廠模式在于可以根據(jù)輸入?yún)?shù)或者應(yīng)用程序配置的不同來創(chuàng)建一種專門用來實(shí)現(xiàn)化并返回其它類的實(shí)例的類。
工廠模式的例子:
<?php
class FactoryBasic {
public static function create($config) {
}
}
比如這里是一個(gè)描述形狀對(duì)象的工廠,它希望根據(jù)傳入的參數(shù)個(gè)數(shù)不同來創(chuàng)建不同的形狀。
<?php
// 定義形狀的公共功能:獲取周長(zhǎng)和面積。
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);
}
}
// 根據(jù)傳入的參數(shù)個(gè)數(shù)不同來創(chuàng)建不同的形狀。
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;
}
}
}
// 矩形對(duì)象
$c = FactoryShape::create(4, 2);
var_dump($c->getArea());
// 圓對(duì)象
$o = FactoryShape::create(2);
var_dump($o->getArea());
使用工廠模式使得在調(diào)用方法時(shí)變得更容易,因?yàn)樗挥幸粋€(gè)類和一個(gè)方法,若沒有使用工廠模式,則要在調(diào)用時(shí)決定應(yīng)該調(diào)用哪個(gè)類和哪個(gè)方法;使用工廠模式還使得未來對(duì)應(yīng)用程序做改變時(shí)更加容易,比如要增加一種形狀的支持,只需要修改工廠類中的create()一個(gè)方法,而沒有使用工廠模式,則要修改調(diào)用形狀的代碼塊。
3. 觀察者模式
觀察者模式為您提供了避免組件之間緊密耦合的另一種方法。該模式非常簡(jiǎn)單:一個(gè)對(duì)象通過添加一個(gè)方法(該方法允許另一個(gè)對(duì)象,即觀察者注冊(cè)自己)使本身變得可觀察。當(dāng)可觀察的對(duì)象更改時(shí),它會(huì)將消息發(fā)送到已注冊(cè)的觀察者。這些觀察者使用該信息執(zhí)行的操作與可觀察的對(duì)象無關(guān)。結(jié)果是對(duì)象可以相互對(duì)話,而不必了解原因。
一個(gè)簡(jiǎn)單的示例:當(dāng)聽眾在收聽電臺(tái)時(shí)(即電臺(tái)加入一個(gè)新聽眾),它將發(fā)送出一條提示消息,通過發(fā)送消息的日志觀察者可以觀察這些消息。
<?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;
}
}
}
}
// 模擬一個(gè)可以被觀察的類:RadioStation
class RadioStation extends Observable {
public function addListener($listener) {
foreach ($this->observers as $observer) {
$observer->onListen($this, $listener);
}
}
}
// 模擬一個(gè)觀察者類
class RadioStationLogger extends Observer {
protected $name = 'logger';
public function onListen($sender, $args) {
echo $args, ' join the radiostation.<br/>';
}
}
// 模擬另外一個(gè)觀察者類
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');
?>
更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《php面向?qū)ο蟪绦蛟O(shè)計(jì)入門教程》、《PHP數(shù)組(Array)操作技巧大全》、《PHP基本語法入門教程》、《PHP運(yùn)算與運(yùn)算符用法總結(jié)》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫操作入門教程》及《php常見數(shù)據(jù)庫操作技巧匯總》
希望本文所述對(duì)大家PHP程序設(shè)計(jì)有所幫助。
相關(guān)文章
淺析Dos下運(yùn)行php.exe,出現(xiàn)沒有找到php_mbstring.dll 錯(cuò)誤的解決方法
本篇文章是對(duì)在Dos下運(yùn)行php.exe,出現(xiàn)沒有找到php_mbstring.dll 錯(cuò)誤的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
Linux下CoreSeek及PHP擴(kuò)展模塊的安裝
前提條件是系統(tǒng)己安裝完成apache mysql php的WEB服務(wù)。我是以yum來安裝的。如果你沒有安裝過請(qǐng)按照下面給出的鏈接先完成基本的LAMP環(huán)境的安裝2012-09-09
解析PHP強(qiáng)制轉(zhuǎn)換類型及遠(yuǎn)程管理插件的安全隱患
這篇文章主要介紹了PHP強(qiáng)制轉(zhuǎn)換類型及遠(yuǎn)程管理插件的安全隱患,需要的朋友可以參考下2014-06-06
php上傳后臺(tái)無法收到數(shù)據(jù)解決方法
在本篇文章里小編給大家整理的是關(guān)于php無法收到數(shù)據(jù)的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們參考學(xué)習(xí)下。2019-10-10
php中var_export與var_dump的區(qū)別分析
php中var_export與var_dump的區(qū)別分析,在大多數(shù)情況下,多用var_dump來輸出,因?yàn)樗草敵隽藬?shù)據(jù)類型。2010-08-08
PHP實(shí)現(xiàn)抓取迅雷VIP賬號(hào)的方法
這篇文章主要介紹了PHP實(shí)現(xiàn)抓取迅雷VIP賬號(hào)的方法,實(shí)例分析了php基于采集類Snoopy實(shí)現(xiàn)頁面抓取及正則匹配的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
php自定義函數(shù)實(shí)現(xiàn)漢字轉(zhuǎn)換utf8編碼的方法
這篇文章主要介紹了php自定義函數(shù)實(shí)現(xiàn)漢字轉(zhuǎn)換utf8編碼的方法,涉及php針對(duì)字符串的遍歷、截取及編碼轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2016-09-09

