簡單工廠模式:
①抽象基類:類中定義抽象一些方法,用以在子類中實現
②繼承自抽象基類的子類:實現基類中的抽象方法
③工廠類:用以實例化對象
使用工廠模式的目的或目標?
工廠模式的最大優點在於創建對象上面,就是把創建對象的過程封裝起來,這樣隨時可以產生一個新的對象。
減少代碼進行復制粘帖,耦合關系重,牽一發動其他部分代碼。
通俗的說,以前創建一個對象要使用new,現在把這個過程封裝起來了。
假設不使用工廠模式:那麼很多地方調用類a,代碼就會這樣子創建一個實例:new a(),假設某天需要把a類的名稱修改,意味著很多調用的代碼都要修改。
工廠模式的優點就在創建對象上。
工廠模式的優點就在創建對象上。建立一個工廠(一個函數或一個類方法)來制造新的對象,它的任務就是把對象的創建過程都封裝起來,
創建對象不是使用new的形式了。而是定義一個方法,用於創建對象實例。
每個類可能會需要連接數據庫。那麼就將連接數據庫封裝在一個類中。以後在其他類中通過類名:
為什麼引入抽象的概念?
想一想,在現實生活中,當我們無法確定某個具體的東西的時候,往往把一類東西歸於抽象類別。
工廠方法:
比如你的工廠叫做“香煙工廠”,那麼可以有“七匹狼工廠”“中華工廠”等,但是,這個工廠只生廠一種商品:香煙;
抽象工廠:無法描述它到底生產什麼產品,它生產很多類型的產品(所以抽象工廠就會生成子工廠)。
你的工廠是綜合型的,是生產“一系列”產品,而不是“一個”,比如:生產“香煙”,還有“啤酒”等。然後它也可以有派生出來的具體的工廠,但這些工廠都是生產這一系列產品,只是可能因為地域不一樣,為了適應當地人口味,味道也不太一樣。
工廠模式:理解成只生成一種產品的工廠。比如生產香煙的。
工廠方法:工廠的一種產品生產線 。比如鍵盤的生成過程。
別人會反駁:吃飽了沒事干,一定要修改類名稱呢?這個說不定。一般都不會去修改類名稱。
其實工廠模式有很多變體,抓住精髓才是關鍵:只要是可以根據不同的參數生成不同的類實例,那麼就符合工廠模式的設計思想。
這樣子讓我聯想到框架中經常會有負責生成具體類實例的方法供調用。
看完文章再回頭來看下這張圖,效果會比較好
采用封裝方式
<?php
class Calc{
/**
* 計算結果
*
* @param int|float $num1
* @param int|float $num2
* @param string $operator
* @return int|float
*/
public function calculate($num1,$num2,$operator){
try {
$result=0;
switch ($operator){
case '+':
$result= $num1+$num2;
break;
case '-':
$result= $num1-$num2;
break;
case '*':
$result= $num1*$num2;
break;
case '/':
if ($num2==0) {
throw new Exception("除數不能為0");
}
$result= $num1/$num2;
break;
}
return $result;
}catch (Exception $e){
echo "您輸入有誤:".$e->getMessage();
}
}
}
$test=new Calc();
// echo $test->calculate(2,3,'+');//打印:5
echo $test->calculate(5,0,'/');//打印:您輸入有誤:除數不能為0
?>
優點:以上代碼使用了面向對象的封裝特性,只要有了include這個類,其他頁面就可以隨便使用了
缺點:無法靈活的擴展和維護
比如:想要增加一個“求余”運算,需要在switch語句塊中添加一個分支語句,代碼需要做如下改動
<?php
class Calc{
public function calculate($num1,$num2,$operator){
try {
$result=0;
switch ($operator){
//......省略......
case '%':
$result= $num1%$num2;
break;
//......省略......
}
}catch (Exception $e){
echo "您輸入有誤:".$e->getMessage();
}
}
}
?>
代碼分析:用以上方法實現給計算器添加新的功能運算有以下幾個缺點
①需要改動原有的代碼塊,可能會在為了“添加新功能”而改動原有代碼的時候,不小心將原有的代碼改錯了
②如果要添加的功能很多,比如:‘乘方’,‘開方’,‘對數’,‘三角函數’,‘統計’,或者添加一些程序員專用的計算功能,比如:And, Or, Not, Xor,這樣就需要在switch語句中添加N個分支語句。想象下,一個計算功能的函數如果有二三十個case分支語句,代碼將超過一屏,不僅令代碼的可讀性大大降低,關鍵是,為了添加小功能,還得讓其余不相關都參與解釋,這令程序的執行效率大大降低
解決途徑:采用OOP的繼承和多態思想
<?php
/**
* 操作類
* 因為包含有抽象方法,所以類必須聲明為抽象類
*/
abstract class Operation{
//抽象方法不能包含函數體
abstract public function getValue($num1,$num2);//強烈要求子類必須實現該功能函數
}
/**
* 加法類
*/
class OperationAdd extends Operation {
public function getValue($num1,$num2){
return $num1+$num2;
}
}
/**
* 減法類
*/
class OperationSub extends Operation {
public function getValue($num1,$num2){
return $num1-$num2;
}
}
/**
* 乘法類
*/
class OperationMul extends Operation {
public function getValue($num1,$num2){
return $num1*$num2;
}
}
/**
* 除法類
*/
class OperationDiv extends Operation {
public function getValue($num1,$num2){
try {
if ($num2==0){
throw new Exception("除數不能為0");
}else {
return $num1/$num2;
}
}catch (Exception $e){
echo "錯誤信息:".$e->getMessage();
}
}
}
?>
這裡采用了面向對象的繼承特性,首先聲明一個虛擬基類,在基類中指定子類務必實現的方法(getValue())
分析:通過采用面向對象的繼承特性,我們可以很容易就能對原有程序進行擴展,比如:‘乘方’,‘開方’,‘對數’,‘三角函數’,‘統計’等等。
<?php
/**
* 求余類(remainder)
*
*/
class OperationRem extends Operation {
public function getValue($num1,$num2){
return $num1%$num12;
}
}
?>
我們只需要另外寫一個類(該類繼承虛擬基類),在類中完成相應的功能(比如:求乘方的運算),而且大大的降低了耦合度,方便日後的維護及擴展
現在還有一個問題未解決,就是如何讓程序根據用戶輸入的操作符實例化相應的對象呢?
解決辦法:使用一個單獨的類來實現實例化的過程,這個類就是工廠
代碼如下:
<?php
/**
* 工程類,主要用來創建對象
* 功能:根據輸入的運算符號,工廠就能實例化出合適的對象
*
*/
class Factory{
public static function createObj($operate){
switch ($operate){
case '+':
return new OperationAdd();
break;
case '-':
return new OperationSub();
break;
case '*':
return new OperationSub();
break;
case '/':
return new OperationDiv();
break;
}
}
}
$test=Factory::createObj('/');
$result=$test->getValue(23,0);
echo $result;
?>
再擴展一些
比如如何根據玩家輸入的內容(盡管可以轉化為其他字符串),來確定要制造的兵種,玩家不會輸入代碼:new Marine()。
和星際一樣,PHP也沒有終極兵種,如果類和接口是兵種的話,那麼設計模式就是你的戰術和控制,它可以讓你靠各種兵種的搭配獲勝。
待解決的問題:在人族的兵營,我們靠相應玩家的輸入來動態確定要造的兵種,假設是機槍兵和火焰兵。
思路:動態的根據傳遞的數據,新建相應的類的對象。
簡單工廠模式示例:
我們把機槍兵類的代碼放入一個文件,Marine.php,它的代碼如下:
<?php
class Marine {
//機槍兵攻擊的方法
public function attack()
{
echo 'Marine attack';
}
}
?>
我們把火焰兵類的代碼放入一個文件,Firebat.php,它的代碼如下:
<?php
class Firebat {
//火焰兵攻擊的方法
public function attack()
{
echo 'Firebat attack';
}
}
?>
主文件中的內容如下:
<?php
//兵種制造器的類
class BarracksCreator {
//制造兵種的方法
public create($createWhat)
{
//根據輸入的參數,動態的把需要的類的定義文件載入
require_once($createWhat.'.php');
//根據輸入的參數,動態的返回需要的類的對象
return new $createWhat;
}
}
//新建一個兵種制造器對象
$creator = new BarracksCreator();
//靠接收參數制造一個火焰兵對象
$troop1 = $creator->create('Marine');
$troop1->attack();
//靠接收參數制造一個機槍兵對象
$troop2 = $creator->create('Firebat');
$troop2->attack();
?>
用途總結:簡單工廠模式可以將新建對象的任務進行封裝,一旦需要增加新的返回類,只要修改負責新建對象的那部分代碼。
實現總結:需要一個自動根據參數返回新建對象的工廠,比如上面兵種制造器BarracksCreator,使用的時候只需要將參數傳遞給他的生產方法create(),無需考慮具體的生產細節。