在軟件工程中,建立型設計模式承擔着對象建立的職責,嘗試建立適合程序上下文的對象,對象建立設計模式的產生是因爲軟件工程設計的問題,具體說是向設計中增長複雜度,建立型設計模式解決了程序設計中對象建立的問題。PHP的設計模式有不少種,本文取最簡單的三種模式: 工廠模式、單例模式和註冊樹模式進行簡單的講解。php
用工廠方法或者類生成對象,而不是代碼中直接使用 new 關鍵字mysql
好比我們要作一個鏈接數據庫的操做,平時都是用mysql。那忽然有一天mysql收費了,咋辦?那咱們就用Sqlite做來數據庫。若是以傳統的方式建立一個數據庫鏈接類的話是須要使用關鍵字new MysqlDrive()
把這個數據庫實例化,一旦數據庫變動成的話就須要把全部的new MysqlDrive()
變動成new SqliteDrive()
這樣替換比較麻煩,而且還容易出錯。因此,下面的工廠模式能夠很好的解決這一問題。git
廢話很少說,演示一遍就明白了github
先看一下目錄結構吧本文演示的所有是遵循PSR-4的編碼規範,固然,是在vendor/
目錄下,若是沒有請本身行執行命令composer dumpatuoload
若是您沒有安裝composer那就請看下面文章:sql
如下是個人目錄結構: bootstrap
自動忽略DependencyInjection 跟HttpFoundation目錄,這是我計劃下次寫的東西,嘻嘻...設計模式
先看看主要文件的代碼吧:composer
tests/
這個目錄是爲了讓我們方便測試的目錄,也就是單元測試目錄框架
文件: tests/bootstrap.php
include_once __DIR__."/../../../composer/ClassLoader.php"; $loader = new \Composer\Autoload\ClassLoader(); $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); $classMaps = [ 'Dudulu\\' => [$vendorDir."/src/"] ]; foreach ($classMaps as $namespace => $path) { $loader->setPsr4($namespace, $path); } $loader->register(true);
我們須要測試的驅動類MysqlDerive.php
文件: src/DesignPatterns\FactoryMode\DB\MysqlDrive.php
namespace Dudulu\DesignPatterns\FactoryMode; use Dudulu\DesignPatterns\DB\MysqlDrive; /** * Class Factory * @package Dudulu\DesignPatterns\FactoryMode */ class Factory { /** * @return MysqlDrive */ public static function database() { return new MysqlDrive(); } }
爲了強制讓每種驅動都要有一個鏈接方式,因此咱們須要一個Interface。
文件: src/DesignPatterns/DB/Interfaces/DriveInterface.php
namespace Dudulu\DesignPatterns\DB\Interfaces; /** * Interface DriveInterface * @package Dudulu\DesignPatterns\DB\Interfaces */ interface DriveInterface { /** * @return mixed */ public function connection(); }
先看一個普通demo,直接實例化MysqlDrive
對象
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\DB\MysqlDrive; class FactoryTest extends PHPUnit_Framework_TestCase { public function testConnection() { $db = new MysqlDrive(); $this->assertEquals(true, $db->connection()); } }
進入src/tests/DesignPattens
目錄.
命令行執行: phpunit FactoryTest.php
能夠發現這樣是好使的...
工廠模式 是一種類,它具備爲您建立對象的某些方法。您可使用工廠類建立對象,而不直接使用 new。這樣,若是您想要更改所建立的對象類型,只需更改該工廠便可。使用該工廠的全部代碼會自動更改。
首先我們先建立一個Factory.php
做爲工廠類,而後裏邊實現一個靜態方法對相數據庫驅動進行實例化。
文件: src/DesignPatterns/FactoryMode/Factory.php
namespace Dudulu\DesignPatterns\FactoryMode; use Dudulu\DesignPatterns\DB\MysqlDrive; /** * Class Factory * @package Dudulu\DesignPatterns\FactoryMode */ class Factory { /** * @return MysqlDrive */ public static function database() { return new MysqlDrive(); } }
建立完後,我們回到單元測試文件FactoryTest.php
文件: tests/DesignPattens/FactoryTest.php
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\FactoryMode\Factory; class FactoryTest extends PHPUnit_Framework_TestCase { public function testConnection() { $db = Factory::database(); $this->assertEquals(true, $db->connection()); } }
再執行一下單元測試命令發現,也能返回成功,這樣的話咱們就能很方便的修改任何驅動了。若是咱們從mysql換到sqlite了,那麼,我們主要的代碼都不須要更變,只要在Factory.php
把connection
方法的邊接方式修改完就行了,很是方便。
使某個類的對象僅容許建立一個
某些應用程序資源是獨佔的,由於有且只有一個此類型的資源。好比,數據庫的鏈接的獨佔。但願在應用程序中共享數據庫鏈接,由於在保持鏈接打開或關閉時,它是一種開銷,在獲取單個頁面的過程當中更是如此。
好比我們鏈接數據庫時,若是不使用單例模式,多個地方都對數據類進行了實例化,那麼這樣會造能不少資源浪費,爲了解決這問題,對於數據庫類咱們只須要實例化一次,後面再次調用它是若是已經實例化,那就直接返回。
來,咱們拿一個類來玩一玩...
文件: src/DesignPatterns/SingletonMode/SingletonDB.php
咱們建立這個類,而後將構造方法私有化,這樣的話咱們就沒法使用
new
關鍵字對這個類進行實例化了。
namespace Dudulu\DesignPatterns\SingletonMode; use Dudulu\DesignPatterns\DB\Interfaces\DriveInterface; /** * Class SingletonDB * @package Dudulu\DesignPatterns\SingletonMode */ class SingletonDB implements DriveInterface { /** * SingletonDB constructor. */ private function __construct() { } /** * @return bool */ public function connection() { return true; } }
那麼咱們要如何使用這個對象呢?咱們須要一個受保護的成員和一個靜態方法:
/** * @var SingletonDB */ protected static $db; /** * @return SingletonDB */ public static function getInstance() { if (self::$db) { return self::$db; } self::$db = new self(); return self::$db; }
這個方法很好理解,簡單意思就是若是當前屬性已經被設置過了,那就再也不進行實例化,而是直接返回,不然實例化當前對象並返回。
與測試上面的方法同樣,測玩玩...
建立測試的文件: tests/DesignPattens/SingletonDBTest.php
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\SingletonMode\SingletonDB; /** * Class SingletonDBTest */ class SingletonDBTest extends PHPUnit_Framework_TestCase { /** * @return void */ public function testConnection() { $db = SingletonDB::getInstance(); $this->assertEquals(true, $db->connection()); } }
執行命令: phpunit SingletonDBTest.php
發現也是能夠執行成功的。
若是不信的話,你能夠試試多執行幾回SingletonDB::getInstance();
而後在 getInstance()
方法體裏作一個計數器,看看它實例化過幾回。
主要用來解決全局共享和交換對象
這個也很好理解,由於咱們在框架中常常用到。
註冊樹模式固然也叫註冊模式,註冊器模式。之因此我在這裏矯情一下它的名稱,是由於我感受註冊樹這個名稱更容易讓人理解。註冊樹模式經過將對象實例註冊到一棵全局的對象樹上,須要的時候從對象樹上採摘的模式設計方法。
我們結合工廠模式及單例模式作一個小例子:
建立文件: src/DesignPatterns/RegisterMode/Register.php
namespace Dudulu\DesignPatterns\RegisterMode; /** * Class Register * @package Dudulu\DesignPatterns\RegisterMode */ class Register { /** * @var array */ protected static $classMaps = []; /** * @param $alias * @param $class * @return void */ public static function set($alias, $class ) { self::$classMaps[$alias] = $class; } /** * @param $alias * @return mixed */ public static function get($alias ) { return self::$classMaps[$alias]; } }
而後建立一個單例模式的DB類DemoDB.php
:
文件: src/DesignPatterns/DB/DemoDB.php
namespace Dudulu\DesignPatterns\DB; use Dudulu\DesignPatterns\DB\Interfaces\DriveInterface; class DemoDB implements DriveInterface { /** * @var DemoDB */ protected static $db; /** * SingletonDB constructor. */ private function __construct() { } /** * @return DemoDB */ public static function getInstance() { if (self::$db) { return self::$db; } self::$db = new self(); return self::$db; } /** * @return bool */ public function connection() { return true; } }
再工廠模式文件上加入註冊方式代碼:
文件: src/DesignPatterns/FactoryMode/Factory.php
use Dudulu\DesignPatterns\RegisterMode\Register; /** * @return DemoDB */ public static function testDb() { $db = DemoDB::getInstance(); Register::set('DB', $db); return $db; }
最後我們驗證一下:
在tests/
目錄下建立RegisterTest.php
文件
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\RegisterMode\Register; use Dudulu\DesignPatterns\DB\DemoDB; /** * Class RegisterTest */ class RegisterTest extends PHPUnit_Framework_TestCase { /** * @return void */ public function testConnection() { Register::set('DB', DemoDB::getInstance()); $db = Register::get('DB'); $this->assertEquals(true, $db->connection()); } }
OK 走一個phpunit RegisterTest.php
返回OK,好棒好棒...☆〜(ゝ。∂)
__ / ))) _ `/ イ~ (((ヽ ( ノ  ̄Y\ | (\ ∧_∧ | ) ヽ ヽ`( `o´ )/ノ/ \ | ⌒Y⌒ / / |ヽ | ノ/ \トー仝ーイ |
GitHub: https://github.com/icowan/dudulu
若是有時間,下次我再寫些關於其餘設計模式的文章...
原文地址: 設計模式之工廠模式、單例模式、註冊樹模式
歡迎關注我站點: LatteCake
歡迎關注公衆號: 聰聰實驗室