摘要:的設(shè)計(jì)模式有很多種,本文取最簡單的三種模式工廠模式單例模式和注冊(cè)樹模式進(jìn)行簡單的講解。文件創(chuàng)建完后,咱們回到單元測(cè)試文件文件再執(zhí)行一下單元測(cè)試命令發(fā)現(xiàn),也能返回成功,這樣的話我們就能很方便的修改任何驅(qū)動(dòng)了。
php 設(shè)計(jì)模式之工廠模式、單例模式、注冊(cè)樹模式
在軟件工程中,創(chuàng)建型設(shè)計(jì)模式承擔(dān)著對(duì)象創(chuàng)建的職責(zé),嘗試創(chuàng)建適合程序上下文的對(duì)象,對(duì)象創(chuàng)建設(shè)計(jì)模式的產(chǎn)生是由于軟件工程設(shè)計(jì)的問題,具體說是向設(shè)計(jì)中增加復(fù)雜度,創(chuàng)建型設(shè)計(jì)模式解決了程序設(shè)計(jì)中對(duì)象創(chuàng)建的問題。PHP的設(shè)計(jì)模式有很多種,本文取最簡單的三種模式: 工廠模式、單例模式和注冊(cè)樹模式進(jìn)行簡單的講解。
工廠模式用工廠方法或者類生成對(duì)象,而不是代碼中直接使用 new 關(guān)鍵字
比如咱們要做一個(gè)連接數(shù)據(jù)庫的操作,平時(shí)都是用mysql。那突然有一天mysql收費(fèi)了,咋辦?那我們就用Sqlite作來數(shù)據(jù)庫。如果以傳統(tǒng)的方式創(chuàng)建一個(gè)數(shù)據(jù)庫連接類的話是需要使用關(guān)鍵字new MysqlDrive()把這個(gè)數(shù)據(jù)庫實(shí)例化,一旦數(shù)據(jù)庫變更成的話就需要把所有的new MysqlDrive()變更成new SqliteDrive() 這樣替換比較麻煩,并且還容易出錯(cuò)。所以,下面的工廠模式可以很好的解決這一問題。
廢話不多說,演示一遍就明白了
先看一下目錄結(jié)構(gòu)吧本文演示的全部是遵循PSR-4的編碼規(guī)范,當(dāng)然,是在vendor/目錄下,如果沒有請(qǐng)自己行執(zhí)行命令composer dumpatuoload 如果您沒有安裝composer那就請(qǐng)看下面文章:
《集成 Composer 包依賴管理》
以下是我的目錄結(jié)構(gòu):
自動(dòng)忽略DependencyInjection 跟HttpFoundation目錄,這是我計(jì)劃下次寫的東西,嘻嘻...
先看看主要文件的代碼吧:
tests/這個(gè)目錄是為了讓咱們方便測(cè)試的目錄,也就是單元測(cè)試目錄
文件: tests/bootstrap.php
include_once __DIR__."/../../../composer/ClassLoader.php"; $loader = new ComposerAutoloadClassLoader(); $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); $classMaps = [ "Dudulu" => [$vendorDir."/src/"] ]; foreach ($classMaps as $namespace => $path) { $loader->setPsr4($namespace, $path); } $loader->register(true);
咱們需要測(cè)試的驅(qū)動(dòng)類MysqlDerive.php
文件: src/DesignPatternsFactoryModeDBMysqlDrive.php
namespace DuduluDesignPatternsFactoryMode; use DuduluDesignPatternsDBMysqlDrive; /** * Class Factory * @package DuduluDesignPatternsFactoryMode */ class Factory { /** * @return MysqlDrive */ public static function database() { return new MysqlDrive(); } }
為了強(qiáng)制讓每種驅(qū)動(dòng)都要有一個(gè)連接方式,所以我們需要一個(gè)Interface。
文件: src/DesignPatterns/DB/Interfaces/DriveInterface.php
namespace DuduluDesignPatternsDBInterfaces; /** * Interface DriveInterface * @package DuduluDesignPatternsDBInterfaces */ interface DriveInterface { /** * @return mixed */ public function connection(); }普通模式
先看一個(gè)普通demo,直接實(shí)例化MysqlDrive對(duì)象
include_once "../bootstrap.php"; use DuduluDesignPatternsDBMysqlDrive; class FactoryTest extends PHPUnit_Framework_TestCase { public function testConnection() { $db = new MysqlDrive(); $this->assertEquals(true, $db->connection()); } }
進(jìn)入src/tests/DesignPattens目錄.
命令行執(zhí)行: phpunit FactoryTest.php可以發(fā)現(xiàn)這樣是好使的...
工廠模式工廠模式 是一種類,它具有為您創(chuàng)建對(duì)象的某些方法。您可以使用工廠類創(chuàng)建對(duì)象,而不直接使用 new。這樣,如果您想要更改所創(chuàng)建的對(duì)象類型,只需更改該工廠即可。使用該工廠的所有代碼會(huì)自動(dòng)更改。
首先咱們先創(chuàng)建一個(gè)Factory.php作為工廠類,然后里邊實(shí)現(xiàn)一個(gè)靜態(tài)方法對(duì)相數(shù)據(jù)庫驅(qū)動(dòng)進(jìn)行實(shí)例化。
文件: src/DesignPatterns/FactoryMode/Factory.php
namespace DuduluDesignPatternsFactoryMode; use DuduluDesignPatternsDBMysqlDrive; /** * Class Factory * @package DuduluDesignPatternsFactoryMode */ class Factory { /** * @return MysqlDrive */ public static function database() { return new MysqlDrive(); } }
創(chuàng)建完后,咱們回到單元測(cè)試文件FactoryTest.php
文件: tests/DesignPattens/FactoryTest.php
include_once "../bootstrap.php"; use DuduluDesignPatternsFactoryModeFactory; class FactoryTest extends PHPUnit_Framework_TestCase { public function testConnection() { $db = Factory::database(); $this->assertEquals(true, $db->connection()); } }
再執(zhí)行一下單元測(cè)試命令發(fā)現(xiàn),也能返回成功,這樣的話我們就能很方便的修改任何驅(qū)動(dòng)了。如果我們從mysql換到sqlite了,那么,咱們主要的代碼都不需要更變,只要在Factory.php把connection方法的邊接方式修改完就好了,非常方便。
單例模式使某個(gè)類的對(duì)象僅允許創(chuàng)建一個(gè)
某些應(yīng)用程序資源是獨(dú)占的,因?yàn)橛星抑挥幸粋€(gè)此類型的資源。比如,數(shù)據(jù)庫的連接的獨(dú)占。希望在應(yīng)用程序中共享數(shù)據(jù)庫連接,因?yàn)樵诒3诌B接打開或關(guān)閉時(shí),它是一種開銷,在獲取單個(gè)頁面的過程中更是如此。
比如咱們連接數(shù)據(jù)庫時(shí),如果不使用單例模式,多個(gè)地方都對(duì)數(shù)據(jù)類進(jìn)行了實(shí)例化,那么這樣會(huì)造能很多資源浪費(fèi),為了解決這問題,對(duì)于數(shù)據(jù)庫類我們只需要實(shí)例化一次,后面再次調(diào)用它是如果已經(jīng)實(shí)例化,那就直接返回。
來,我們拿一個(gè)類來玩一玩...
文件: src/DesignPatterns/SingletonMode/SingletonDB.php
我們創(chuàng)建這個(gè)類,然后將構(gòu)造方法私有化,這樣的話我們就無法使用new關(guān)鍵字對(duì)這個(gè)類進(jìn)行實(shí)例化了。
namespace DuduluDesignPatternsSingletonMode; use DuduluDesignPatternsDBInterfacesDriveInterface; /** * Class SingletonDB * @package DuduluDesignPatternsSingletonMode */ class SingletonDB implements DriveInterface { /** * SingletonDB constructor. */ private function __construct() { } /** * @return bool */ public function connection() { return true; } }
那么我們要如何使用這個(gè)對(duì)象呢?我們需要一個(gè)受保護(hù)的成員和一個(gè)靜態(tài)方法:
/** * @var SingletonDB */ protected static $db; /** * @return SingletonDB */ public static function getInstance() { if (self::$db) { return self::$db; } self::$db = new self(); return self::$db; }
這個(gè)方法很好理解,簡單意思就是如果當(dāng)前屬性已經(jīng)被設(shè)置過了,那就不再進(jìn)行實(shí)例化,而是直接返回,否則實(shí)例化當(dāng)前對(duì)象并返回。
與測(cè)試上面的方法一樣,測(cè)玩玩...
創(chuàng)建測(cè)試的文件: tests/DesignPattens/SingletonDBTest.php
include_once "../bootstrap.php"; use DuduluDesignPatternsSingletonModeSingletonDB; /** * Class SingletonDBTest */ class SingletonDBTest extends PHPUnit_Framework_TestCase { /** * @return void */ public function testConnection() { $db = SingletonDB::getInstance(); $this->assertEquals(true, $db->connection()); } }
執(zhí)行命令: phpunit SingletonDBTest.php 發(fā)現(xiàn)也是可以執(zhí)行成功的。
如果不信的話,你可以試試多執(zhí)行幾次SingletonDB::getInstance(); 然后在 getInstance() 方法體里做一個(gè)計(jì)數(shù)器,看看它實(shí)例化過幾次。
注冊(cè)樹模式主要用來解決全局共享和交換對(duì)象
這個(gè)也很好理解,因?yàn)槲覀冊(cè)诳蚣苤薪?jīng)常用到。
注冊(cè)樹模式當(dāng)然也叫注冊(cè)模式,注冊(cè)器模式。之所以我在這里矯情一下它的名稱,是因?yàn)槲腋杏X注冊(cè)樹這個(gè)名稱更容易讓人理解。注冊(cè)樹模式通過將對(duì)象實(shí)例注冊(cè)到一棵全局的對(duì)象樹上,需要的時(shí)候從對(duì)象樹上采摘的模式設(shè)計(jì)方法。
咱們結(jié)合工廠模式及單例模式做一個(gè)小例子:
創(chuàng)建文件: src/DesignPatterns/RegisterMode/Register.php
namespace DuduluDesignPatternsRegisterMode; /** * Class Register * @package DuduluDesignPatternsRegisterMode */ 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]; } }
然后創(chuàng)建一個(gè)單例模式的DB類DemoDB.php:
文件: src/DesignPatterns/DB/DemoDB.php
namespace DuduluDesignPatternsDB; use DuduluDesignPatternsDBInterfacesDriveInterface; 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; } }
再工廠模式文件上加入注冊(cè)方式代碼:
文件: src/DesignPatterns/FactoryMode/Factory.php
use DuduluDesignPatternsRegisterModeRegister; /** * @return DemoDB */ public static function testDb() { $db = DemoDB::getInstance(); Register::set("DB", $db); return $db; }
最后咱們驗(yàn)證一下:
在tests/目錄下創(chuàng)建RegisterTest.php文件
include_once "../bootstrap.php"; use DuduluDesignPatternsRegisterModeRegister; use DuduluDesignPatternsDBDemoDB; /** * 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 走一個(gè)phpunit RegisterTest.php
返回OK,好棒好棒...☆?(ゝ。?)
__ / ))) _ `/ イ~ (((ヽ ( ?  ̄Y\ | (\ ∧_∧?。? ヽ ヽ`( `o′ )/ノ/ \ | ⌒Y⌒ / / |ヽ | ?/ ?。堀醛`仝ーイ |
GitHub: https://github.com/icowan/dudulu
如果有時(shí)間,下次我再寫些關(guān)于其他設(shè)計(jì)模式的文章...
原文地址: 設(shè)計(jì)模式之工廠模式、單例模式、注冊(cè)樹模式
歡迎關(guān)注我站點(diǎn): LatteCake
歡迎關(guān)注公眾號(hào): 聰聰實(shí)驗(yàn)室
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30285.html
摘要:一工廠模式工廠方法或類中生成對(duì)象,而不是在代碼中直接優(yōu)點(diǎn)將某個(gè)常用類,多個(gè)地方需要時(shí),使用工廠模式,方便類的擴(kuò)展與維護(hù)文件目錄正常實(shí)例化類獲取單例模式的類將實(shí)例化后的類注冊(cè)到全局注冊(cè)樹中外部調(diào)用得到對(duì)象獲取全局注冊(cè)樹中的對(duì)象卸載全局注冊(cè)樹中 一、工廠模式:工廠方法或類中生成對(duì)象,而不是在代碼中直接 new 優(yōu)點(diǎn):將某個(gè)常用類,多個(gè)地方需要 new 時(shí),使用工廠模式,方便類的擴(kuò)展與維護(hù)文...
摘要:我們今天也來做一個(gè)萬能遙控器設(shè)計(jì)模式適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。今天要介紹的仍然是創(chuàng)建型設(shè)計(jì)模式的一種建造者模式。設(shè)計(jì)模式的理論知識(shí)固然重要,但 計(jì)算機(jī)程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計(jì)模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對(duì)容器接口對(duì)象進(jìn)行操作,第二類是返回一個(gè)容器接口對(duì)象,上節(jié)我們介紹了...
摘要:我們今天也來做一個(gè)萬能遙控器設(shè)計(jì)模式適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。今天要介紹的仍然是創(chuàng)建型設(shè)計(jì)模式的一種建造者模式。設(shè)計(jì)模式的理論知識(shí)固然重要,但 計(jì)算機(jī)程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計(jì)模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對(duì)容器接口對(duì)象進(jìn)行操作,第二類是返回一個(gè)容器接口對(duì)象,上節(jié)我們介紹了...
摘要:很多接觸的框架就是基于各種模式設(shè)計(jì)形成的。在所有模式設(shè)計(jì)中,有三種基礎(chǔ)設(shè)計(jì)模式,單例模式,工廠模式,注冊(cè)樹模式,其他模式往往基于這幾種模式,今天帶來的是單例模式。工廠模式更多考慮的是擴(kuò)展維護(hù)的問題。 (非原創(chuàng)) 一.單例模式 模式設(shè)計(jì)是什么?初學(xué)者一開始會(huì)被這高大上的名稱給唬住。而對(duì)于有豐富編程經(jīng)驗(yàn)的老鳥來說,模式設(shè)計(jì)又是無處不在。很多接觸的框架就是基于各種模式設(shè)計(jì)形成的。 簡單說,在...
摘要:結(jié)構(gòu)型模式適配器模式橋接模式裝飾模式組合模式外觀模式享元模式代理模式。行為型模式模版方法模式命令模式迭代器模式觀察者模式中介者模式備忘錄模式解釋器模式模式狀態(tài)模式策略模式職責(zé)鏈模式責(zé)任鏈模式訪問者模式。 主要版本 更新時(shí)間 備注 v1.0 2015-08-01 首次發(fā)布 v1.1 2018-03-12 增加新技術(shù)知識(shí)、完善知識(shí)體系 v2.0 2019-02-19 結(jié)構(gòu)...
閱讀 2073·2021-09-09 09:33
閱讀 1135·2019-08-30 15:43
閱讀 2693·2019-08-30 13:45
閱讀 3328·2019-08-29 11:00
閱讀 926·2019-08-26 14:01
閱讀 3590·2019-08-26 13:24
閱讀 507·2019-08-26 11:56
閱讀 2714·2019-08-26 10:27