前段時間看到有人用魔獸來解釋設計模式,感覺很有意思,於是我把它改了改,又添加了些設計模式內容,今天發出來。有些地方借鑒了前人的內容,沒有註明,請前人不要見怪啊。
這裡用大家感興趣的魔獸3來討論PHP的幾種常見的設計模式:單件模式、策略模式、原廠模式、觀察者模式。今天就講這四個吧,以後繼續。
這些設計模式,都是針對物件導向來說的,所以都用PHP5,另外在這裡我想說的是PHP4從2008年8月8日(我記得是和北京奧運會同一天,沒查證,呵呵)的時候官方就發了最後一個PHP4的補丁,這意味這PHP4的時代已經終結,所以,我建議大家現在就別理PHP4吧,就以PHP5來說吧。
一、單件模式:
問題的提出:
某些應用程式資源是獨佔的,因為有且只有一個此類型的資源。例如,通過資料庫控制代碼到資料庫的串連是獨佔的。您希望在應用程式中共用資料庫控制代碼,因為在保持串連開啟或關閉時,它是一種開銷,在擷取單個頁面的過程中更是如此。
問題的解決:
那麼下面我們就開始玩魔獸吧。首先雙擊war3.exe,這時候就開始運行魔獸了。我們用代碼來實現吧。
<?php
class War3
{
public function __construct()
{
echo "War3 is Running.","<br />";
}
}
$war = new War3();
運行!很好,輸出
War3 is Running.
我們已經可以開始遊戲了,但是,如果我在代碼末尾再加入
$war2 = new War3();
$war3 = new War3();
會怎麼樣呢?我們試試,輸出結果:
War3 is Running.
War3 is Running.
War3 is Running.
完了,如果不小心雙擊了兩次就開了3個魔獸,那如果再雙擊幾次,那電腦肯定爆掉。。。我們還是來想想解決方案吧。
既然我們不能這麼隨意的就把這個類執行個體化了,那麼我們就把建構函式改成私人方法。
class War3
{
private function __construct()
{
echo "War3 is Running.","<br />";
}
}
可是私人變數外部是無法訪問的,這樣以來,我們就連一個都打不開了啊。別急,我們再給他加一個不用通過執行個體化,外部也能訪問的函數,那就是靜態函數,
class War3
{
private function __construct()
{
echo "War3 is Running.","<br />";
}
public static function runWar()
{
}
}
通過這個靜態方法runWar()我們來控制類War3的執行個體化,那麼還缺上一個標識,我們再建立一個標識,通過這個標識來表示我們的類是否已經執行個體化,如果執行個體化,直接返回控制代碼就行了。
把類修改成
class War3
{
protected static $_instance = null;
private function __construct()
{
echo "War3 is Running.","<br />";
}
public static function runWar()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
}
當然,我們運行魔獸時的執行個體化也要換種方法,就通過
$war = War3::runWar();
就能開始玩魔獸了,好了,下面把完整的代碼附上來:
<?php
class War3
{
protected static $_instance = null;
private function __construct()
{
echo "War3 is Running.","<br />";
}
public static function runWar()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
}
$war = War3::runWar();
$war2 = War3::runWar();
$war3 = War3::runWar();
運行一下,結果是:
War3 is Running.
太好了,我雙擊了這麼多次,也就只運行了一個魔獸,現在隨便你怎麼開啟,機子都不會爆掉了。
這就是傳說中的單價模式,主要用於一些很佔資源的而且執行個體僅有一個執行個體就夠用的東西,比如,zend framework中的Zend_Controller_Front前端控制器,就是採用單價模式來設計的,大家有興趣的話可以看看那個。
二、策略模式:
問題的提出:
在此模式中,演算法是從複雜類提取的,因而可以方便地替換。例如,如果要更改搜尋引擎中排列頁的方法,則策略模式是一個不錯的選擇。思考一下搜尋引擎的幾個部分 —— 一部分遍曆頁面,一部分對每頁排列,另一部分基於排列的結果排序。在複雜的樣本中,這些部分都在同一個類中。通過使用原則模式,您可將排列部分放入另一個類中,以便更改頁排列的方式,而不影響搜尋引擎的其餘代碼。
問題的解決:
呵呵,不講那麼複雜,剛才魔獸好不容易開啟了,我們還是玩魔獸好了。
下面我們選battle,哇好多種族啊,有人族(Human),獸族(ORC),暗夜精靈族(Nighy Elf),不死族(Undead)。我選精靈族(Nighy Elf),再選一個精靈族和兩個獸族(ORC),一個獸族和我是一家的,另一個精靈族和獸族是另一家的。
每一個玩家在進入遊戲後都會得到一些資源,如一個大廳,五個小精靈(苦工)和一個礦山。這些可以稱為是初始化的一些東西,這裡我們就可以用到策略模式來封裝這些初始化。
進入正題,首先我們來構建一個玩家類:
<?php
class player
{
//玩家名字
protected $_name;
//種族
protected $_race;
//隊伍
protected $army;
//建築
protected $building;
//人口
protected $population;
//黃金
protected $gold;
//木材
protected $wood;
//建構函式,設定所屬種族
public function __construct($race)
{
$this->race = $race;
}
//__get()方法用來擷取保護屬性
private function __get($property_name)
{
if(isset($this->$property_name)) {
return($this->$property_name);
}
else {
return(NULL);
}
}
//__set()方法用來設定保護屬性
private function__set($property_name,$value)
{
$this->$property_name=$value;
}
}
接著,我們再建一個玩家初始化的介面,
<?php
interface initialPlayer
{
//製造初始化的部隊
public function giveArmy($player);
//製造初始化的建築
public function giveBuilding($player);
//初始化資源
public function giveSource($player);
}
好了,到這裡我們就該對這個介面來實現了,為了方便,我只選了兩個種族,就唯寫這兩個種族的初始化了:
首先是精靈族:
<?php
class NighyElfInitial implements initialPlayer
{
//製造初始化的部隊
public function giveArmy($player)
{
//五個小精靈
for($i=0; $i<=5;$i++)
{
$creator = new CreatArms();//這個是建立部隊類,在後面得原廠模式中會用到,這裡我就不多說了
$player->army[] = $creator->Creat('Wisp','./Arms/');
}
}
//製造初始化的建築
public function giveBuilding($player)
{
$creator = new CreatBuildings();
//一個基地
$player->building[] = $creator->Creat('TownHall','./Buildings/');
//一個礦場
$player->building[] = $creator->Creat('Mine','./Buildings/');
}
//初始化人口上限
public function giveSource($player)
{
$player->population= 10;
$player->gold= 1000;
$player->wood= 100;
}
}
接下來是獸族:
<?php
class ORCInitial implements initialPlayer
{
//製造初始化的部隊
public function giveArmy($player)
{
//五個苦工
for($i=0; $i<=5;$i++)
{
$creator = new CreatArms();//這個是建立部隊類,在後面得原廠模式中會用到,這裡我就不多說了
$player->army[] = $creator->Creat('Peon','./Arms/');
}
}
//製造初始化的建築
public function giveBuilding($player)
{
$creator = new CreatBuildings();
//一個基地
$player->building[] = $creator->Creat('TownHall','./Buildings/');
//一個礦場
$player->building[] = $creator->Creat('Mine','./Buildings/');
}
//初始化人口上限
public function giveSource($player)
{
$player->population= 10;
$player->gold= 1000;
$player->wood= 100;
}
}