1. 程式人生 > >簡單工廠模式 - 學習筆記

簡單工廠模式 - 學習筆記

我想 ase 幫助 rto .class 分支 value sqlserve catch

工廠模式主要是為創建對象提供過渡接口,以便將創建對象的具體過程屏蔽隔離起來,達到提高靈活性的目的。

工廠模式可以分為三類:

  • 簡單工廠模式(Simple Factory)
  • 工廠方法模式(Factory Method)
  • 抽象工廠模式(Abstract Factory)

這三種模式從上到下逐步抽象,並且更具一般性。

簡單工廠模式又稱靜態工廠方法模式。重命名上就可以看出這個模式一定很簡單。它存在的目的很簡單:定義一個用於創建對象的接口。工廠方法模式去掉了簡單工廠模式中工廠方法的靜態屬性,使得它可以被子類繼承。這樣在簡單工廠模式裏集中在工廠方法上的壓力可以由工廠方法模式裏不同的工廠子類來分擔。

工廠方法模式仿佛已經很完美的對對象的創建進行了包裝,使得客戶程序中僅僅處理抽象產品角色提供的接口。那我們是否一定要在代碼中遍布工廠呢?大可不必。也許在下面情況下你可以考慮使用工廠方法模式:

  • 當客戶程序不需要知道要使用對象的創建過程。
  • 客戶程序使用的對象存在變動的可能,或者根本就不知道使用哪一個具體的對象。

=================================

簡單工廠模式:
①抽象基類:類中定義抽象一些方法,用以在子類中實現
②繼承自抽象基類的子類:實現基類中的抽象方法
③工廠類:用以實例化對象

看完文章再回頭來看下這張圖,效果會比較好

技術分享

采用封裝方式

<?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,現在把這個過程封裝起來了。
假設不使用工廠模式:那麽很多地方調用類a,代碼就會這樣子創建一個實例:new a(),假設某天需要把a類的名稱修改,意味著很多調用的代碼都要修改。

工廠模式的優點就在創建對象上。
工廠模式的優點就在創建對象上。建立一個工廠(一個函數或一個類方法)來制造新的對象,它的任務就是把對象的創建過程都封裝起來,
創建對象不是使用new的形式了。而是定義一個方法,用於創建對象實例。

每個類可能會需要連接數據庫。那麽就將連接數據庫封裝在一個類中。以後在其他類中通過類名:

為什麽引入抽象的概念?
想一想,在現實生活中,當我們無法確定某個具體的東西的時候,往往把一類東西歸於抽象類別。
工廠方法:
比如你的工廠叫做“香煙工廠”,那麽可以有“七匹狼工廠”“中華工廠”等,但是,這個工廠只生廠一種商品:香煙;
抽象工廠:無法描述它到底生產什麽產品,它生產很多類型的產品(所以抽象工廠就會生成子工廠)。
你的工廠是綜合型的,是生產“一系列”產品,而不是“一個”,比如:生產“香煙”,還有“啤酒”等。然後它也可以有派生出來的具體的工廠,但這些工廠都是生產這一系列產品,只是可能因為地域不一樣,為了適應當地人口味,味道也不太一樣。
工廠模式:理解成只生成一種產品的工廠。比如生產香煙的。
工廠方法:工廠的一種產品生產線 。比如鍵盤的生成過程。

別人會反駁:吃飽了沒事幹,一定要修改類名稱呢?這個說不定。一般都不會去修改類名稱。

其實工廠模式有很多變體,抓住精髓才是關鍵:只要是可以根據不同的參數生成不同的類實例,那麽就符合工廠模式的設計思想。

這樣子讓我聯想到框架中經常會有負責生成具體類實例的方法供調用。

由於前面使用過phpcms,用phpcms的來幫助理解,更加好,如下:
pc_base:load_app_class("order"‘);//參數名稱就是類名稱。將會生成得到order這個實例。傳遞不同的參數得到不同的類實例,這個就符合工廠模式。
pc_base:load_app_class("comment"‘);//生成一個comment類實例
//當然load_app_class這個方法裏面還會結合了單件模式的思想。避免調用n次,就重復創建n個相同的實例


工廠模式我想到的一個典型的應用就是:php可能要鏈接mysql,也可能要鏈接sqlserver,還有其他什麽數據庫。那麽做一個抽象的數據庫類,

這個類就是一個工廠類,專門負責產生不同的對象。
這樣子做很方便擴展。我們在直接鏈接數據庫的時候,不是使用代碼new Mysql($host,$username,$password,$dbname)的形式
而可以動態生成一個連接數據庫的實例。可以是mysql,也可以是連接oracle的。

class DbFactory
{

function static factory($db_class_name)
{

$db_class_name = strtolower($db_class_name);

if (include_once ‘Drivers/‘ . $db_class_name . ‘.php‘) {


$classname = ‘Driver_‘ . $db_class_name;
return new $db_class_name;
} else {
throw new Exception (‘對應的數據庫類沒找到‘);
}


}




}



DbFactory::factory("mysql");

DbFactory::factory("oracle");



在thinkphp框架中也有對應的實現:

Db.class.php就是一個工廠類(也可以叫做數據庫中間層,之所以叫做中間層,是因為可以操作mysql、oracle等各數據庫。而這個類就是中間層作用,屏蔽掉具體的實現。讓程序員可以不改動原來的查詢代碼。中間層來對接mysql、oracle等數據庫。

Db.class.php中有個factory()方法來創建不同的數據庫實例

public function factory($db_config=‘‘) {
// 讀取數據庫配置
$db_config = $this->parseConfig($db_config);
if(empty($db_config[‘dbms‘]))
throw_exception(L(‘_NO_DB_CONFIG_‘));
// 數據庫類型
$this->dbType = ucwords(strtolower($db_config[‘dbms‘]));
$class = ‘Db‘. $this->dbType;
if(is_file(CORE_PATH.‘Driver/Db/‘.$class.‘.class.php‘)) {
// 內置驅動
$path = CORE_PATH;
}else{ // 擴展驅動
$path = EXTEND_PATH;
}
// 檢查驅動類
if(require_cache($path.‘Driver/Db/‘.$class.‘.class.php‘)) {
$db = new $class($db_config);
// 獲取當前的數據庫類型
if( ‘pdo‘ != strtolower($db_config[‘dbms‘]) )
$db->dbType = strtoupper($this->dbType);
else
$db->dbType = $this->_getDsnType($db_config[‘dsn‘]);
if(APP_DEBUG) $db->debug = true;
}else {
// 類沒有定義
throw_exception(L(‘_NOT_SUPPORT_DB_‘).‘: ‘ . $db_config[‘dbms‘]);
}
return $db;
}

還有做支付接口的時候,未來可能對應不同的支付網關:支付寶、財付通、網銀在線等。方便未來擴展,設計成工廠模式。定一個專門生產網關接口的工廠,抽象出來,做成接口形式,讓所有的子類都要實現它的接口。以後加一個支付方式,要使用哪一種支付方式,改變一下參數即可。

書籍<php權威編程>(英文名稱為PHP 5 Power Programming)也提到一個工廠模式的例子,學到一招:在為用戶註冊的時候,分為很多種角色的用戶。比如冊用戶,匿名用戶、管理員用戶等。完全使用可以使用工廠的思想來實現,代碼也容易維護,為每種角色可以生成操作的類。

定義以下幾個類:
UserFactory 用戶工廠類,負責生成不同的用戶類
User:用戶類的基類,所有用戶類都是繼承這個類
不同角色的類:註冊用戶類、匿名用戶類、管理員用戶類

簡單工廠模式 - 學習筆記