php中常見的幾種設計模式
1. 單例模式
單例模式可以說是面嚮物件語言裡最常用、也是最簡單的一種模式。單例就是單個例項,單個物件
的意思,就是說我們去例項化一個類的時候,不管呼叫多少次,都永遠只有一個例項
,
不會有多個
,這樣就節省了記憶體分配開支。
先簡單說下單例模式
的原理:將建構函式__construct
設定為私有的private
, 對外提供一個static靜態方法
比如:getInstance
獲得例項,在getInstance
方法裡來做統一判斷是否有new
一個例項,有的話直接返回,沒有就new
一個,這樣就保證了不管外面呼叫多少次,只保證new了一次
。
直接上程式碼,舉例來說明下:
<?php
/**
* 1.單例模式 Single.php
*/
namespaceLib;
classSingle{
staticprivate $instance;
/**
* 在單例模式下,要將構造方法設為私有的,這樣在外部就不能例項化
*/
privatefunction __construct()
{
}
/**
* 這個是獲取例項化物件的入口,是靜態方法,不需要new,可以直接呼叫
*/
staticpublicfunction getInstance()
{
//判斷一下$instance是否已經存在
if(!isset(self::$instance)){
self::$instance =newSingle();
}
returnself::$instance
}
publicfunctionget()
{
echo 'you get it!';
}
}
在index.php中,我們來例項化呼叫一下。
define('WWWDIR', __DIR__);
include WWWDIR .'/Lib/Load.php';
spl_autoload_register('\\Lib\\Load::autoload');
// 1. 單例模式
$single =Lib\Single::getInstance();
$single2 =Lib\Single::getInstance();
$single->get();//you get it!
$single2->
var_dump($single === $single2);//boolean true
從列印結果我們看出,$single1和$single2其實是同一個引用的,這樣就達到了我們的目的。而且也節約了記憶體。在同一個生命週期中,不管呼叫多次,永遠只有一個例項。
2. 工廠模式
先簡單說下工廠模式
:當我要例項化類的時候,不直接new
這個類,而是通過呼叫另一個類的一個方法來例項化。這就是工廠模式
的核心原理。
這樣的好處有啥呢?
- 假設不使用工廠模式:比如很多地方呼叫類
class_a
,程式碼就會這樣子建立一個例項:new class_a()
, 假設某天需要把class_a
類的名子修改成class_b
,意味著很多呼叫的程式碼都要修改。如果你用工廠模式
,就你只需要改一處就可以了。當然這只是一個很極端的例子,沒人會吃飽了沒事幹會去修改類名。這也是工廠模式最簡單的用法。 - 工廠模式最多的用法,就是根據條件來建立不同的例項,比如你傳入一個mysql,我去例項化mysql類給你,你傳入sql server,那我就例項化sql server類給你。有點像
switch
乾的活。這樣就簡化了邏輯,統一控制,程式碼也比較簡化。
還是直接上程式碼來說明:
<?php
/**
* 2.Factory.php 工廠模式
* User: tonyyang
* Date: 4/2 0002
* Time: 15:19
*/
namespaceLib;
classFactory{
staticpublicfunction create()
{
/**
* 在同樣一個名稱空間下,要這樣直接呼叫。這樣呼叫是會報錯:Lib\FactoryTest1();
* 它去找Lib\Lib\FactoryTest1.php 這個檔案去了。
*/
// return new FactoryTest1();
returnnewFactoryTest2();
}
}
<?php
/**
* Created by PhpStorm.
* User: tonyyang
* Date: 4/2 0002
* Time: 15:22
*/
namespaceLib;
classFactoryTest2{
publicfunction __construct()
{
}
publicfunctionget()
{
echo 'factory get it!';
}
}
//2.工廠模式
//不用工廠模式,我在很多地方呼叫一個類是這樣的。
newLib\FactoryTest1();
newLib\FactoryTest1();
newLib\FactoryTest1();
newLib\FactoryTest1();
//用工廠模式,我不直接去new FactoryTest1這個類,我是通過Factory這個類的create方法來
$test1 =Lib\Factory::create();
$test1->get();
//假如我現在吃飽了犯傻,將FactoryTest1的類名改成FactoryTest2了,普通的寫法是這樣:
newLib\FactoryTest2();
newLib\FactoryTest2();
newLib\FactoryTest2();
newLib\FactoryTest2();
//用了工廠模式,還是一模一樣的寫法,我通過修改Factory類裡的create方法這一處就可以了。
$test1 =Lib\Factory::create();
$test1->get();
工廠模式的第二張方式就是統一管理,通過傳參的方式來例項化,最常見的DB,也可能有Mysql,也有可能有SqlServer:
staticpublicfunction db($dbName)
{
if(empty($dbName)){
returnnewDb\Mysql();
}
//選擇判斷
switch($dbName){
case'mysql':
returnnewDb\Mysql();
break;
case'sql_server':
returnnewDb\SqlServer();
break;
}
}
這樣,我就可以通過引數傳遞的方式,來選擇資料庫連線了。
//通過傳引數的方式來實現切換不同的資料庫例項
$mysql =Lib\Factory::db('mysql');
$sqlServer =Lib\Factory::db('sql_server');
3. 註冊模式
註冊模式
在單例模式
的基礎上進一步拓展了一步,他把所有單例模式
的物件全部儲存在一個索引陣列
中,下次取得時候,直接去數組裡按照索引去取。這樣的好處是在程式中有條理的存放並管理物件。所以,肯定有一個存(set)
和一個取(get)
。一般是先去取
如果沒有,就重新初始化然後存
起來,這樣這個週期中的其他程式碼就可以直接取
了。和redis快取
的道理是一樣的。
我先來實現註冊模式的程式碼:
<?php
/**
* 3. 註冊模式
*
* @package Register.php
*/
namespaceLib;
classRegister
{
protectedstatic $_config;
/**
* 獲取變數值
*
* @param string $name
* @return string
*/
publicstaticfunctionget($name)
{
if(self::isRegister($name)){
returnself::$_config[$name];
}
else{
returnfalse;
}
}
/**
* 設定變數值
*
* @param string $name
* @param string $value
* @return string
*/
publicstaticfunctionset($name, $value)
{
if(!isset($name)||!isset($value)){
returnfalse;
}
self::$_config[$name]= $value;
returnself::$_config[$name];
}
/**
* 判斷變數是否註冊
*
* @param string $name
* @return bool
*/
publicstaticfunction isRegister($name)
{
return isset(self::$_config[$name]);
}
}
上面程式碼我們定義了get
和set
方法。這樣在一個存取模式就定義好了。
在我們的專案當中,像資料庫連線
,redis連線
,配置檔案
等,都是在各個地方被大量使用的,我們完全可以使用註冊模式,將這些變數物件寫到註冊器上面去。
下面我們來呼叫一下:
// 3. 註冊模式
$register =Lib\Register::getInstance();
//資料庫連結提前寫到註冊樹上
$register->set('mysql',newLib\Db\Mysql());
$register->set('sql_server',newLib\Db\SqlServer());
//讀取db類
$register->get('mysql')->select('select * from user');
$register->get('sql_server')->select('select * from user');
//redis類寫到註冊樹上
$register->set('redis',newLib\Cache\Redis());
//讀取redis類
$register->get('redis')->get('redis-cache-2012');
//配置檔案寫到註冊樹上
$config = array(
'db_name'=>'127.0.0.1',
);
$register->set('config', $config);
echo $register->get('config')['db_name'];// php > 5.4
這樣既實現了單例模式
,又使得所有的變數類都統一管理,實現起來更簡潔,而且程式碼效率更高,更省記憶體。
4. 介面卡模式
就是將一些截然不同的函式介面封裝成統一的API,最簡單的例子就是DB類了。我們有mysql
,mysqli
,pdo
等。如果我們專案中同時有這幾種資料庫存在,那麼做一些操作是很蛋疼的,因為它們的都有各自額api.所以,這個時候,我們可以用介面卡模式
將3種不同的資料庫封裝成統一的api, 對外就是統一的方式呼叫。再比如我們專案中可能會用到的快取有redis
,memcache
等,我們也同樣可以把他們封裝成統一的API介面。
我們用db
類為例子來說明下。
//4 . 介面卡模式
$register =Lib\Register::getInstance();
$config = array(
'host'=>'127.0.0.1',
'db_name'=>'test',
'user_name'=>'root',
'password'=>'',
);
//用註冊模式將db配置檔案註冊。
$register->set('db', $config);
//
$register->set('mysql',newLib\Db\MySQL());
$register->set('mysqli',newLib\Db\MySQLi());
$register->set('pdo',newLib\Db\PDO());
//api介面一模一樣
$mysqlInfo = $register->get('mysql')->select('select 1+1;');
$mysqliInfo = $register->get('mysqli')->select('select 1+1;');
$pdoInfo = $register->get('pdo')->select('select 1+1;');
var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);
//工廠模式來做也一樣
$mysql =Lib\Factory::db('mysql');
$mysqli =Lib\Factory::db('mysqli');
$pdo =Lib\Factory::db('pdo');
//api介面一模一樣
$mysqlInfo = $mysql->select('select 1+1;');
$mysqliInfo = $mysqli->select('select 1+1;');
$pdoInfo = $pdo->select('select 1+1;');
var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);