一、PHP常用的四種數據結構
簡介:spl是php的一個標準庫。
官方文檔:http://php.net/manual/zh/book.spl.php
<?php//spl(php標準庫)數據結構/*** 棧(先進后出)*/
$stack = new SplStack();
$stack->push('data1');//入棧(先進后出)
$stack->push('data2');//入棧
$stack->push('data3');//入棧echo $stack->pop();//出棧
echo $stack->pop();//出棧
echo $stack->pop();//出棧/***隊列(先進先出)*/
$queue = new SplQueue();
$queue->enqueue('data4');//入隊列
$queue->enqueue('data5');//入隊列
$queue->enqueue('data6');//入隊列echo $queue->dequeue();//出隊列
echo $queue->dequeue();//出隊列
echo $queue->dequeue();//出隊列
echo $queue->dequeue();//出隊列/*** 堆*/
$heap = new SplMinHeap();
$heap->insert('data8');//入堆
$heap->insert('data9');//入堆
$heap->insert('data10');//入堆echo $heap->extract();//從堆中提取數據
echo $heap->extract();//從堆中提取數據
echo $heap->extract();//從堆中提取數據/*** 固定數組(不論使不使用,都會分配相應的內存空間)*/
$array = new SplFixedArray(15);
$array['0'] = 54;
$array['6'] = 69;
$array['10'] = 32;
var_dump($array);
?
?
二、PHP鏈式操作的實現(原理)
?
1、入口文件 index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載$db = new \Extend\Database();
$db->where('uid < 100000')->->order('uid desc')->limit(100);
?
2、自動加載類 Loader.php
<?php
namespace Extend;
/*** 實現框架的自動加載*/
class Loader
{/*** 實現文件的自動載入*/static function autoload($class){require BASEDIR.'/'.str_replace('\\','/',$class).'.php';}}
?
3、數據庫類Database.php
注:只是原理,并沒有對方法進行具體的封裝,具體的封裝還是看個人喜好去定鏈式查詢的風格。
<?php
namespace Extend;class Database
{/*** 指定查詢條件* @param $where*/function where($where){return $this;}/*** 指定排序條件*/function order($order){return $this;}/*** 指定查詢的限制條數* @param $limit*/function limit($limit){return $this;}}
其實就是對傳過來的條件進行重新的底層封裝,然后再把當前對象返回,使得可以不斷的鏈式查詢。
?
?
三、PHP魔術方法的使用
在php設計模式中,會涉及到很多魔術方法的使用,這里也對經常會用到的魔術方法進行簡單總結。
1、框架入口文件 index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載/*** 魔術方法的使用*/# 實例化Object類
$obj = new \Extend\Object();//當前文件不存在這個類,就會自動執行自動加載函數去包含相應的類文件(即 Extend/Object.php)# __set 和 __get 對不存在的屬性進行接管
$obj->title = 'xiaobudiu'; //當對一個不存在的類屬性賦值時,會自動調用類中定義的__set()
echo $obj->title; //當調用一個不存在的類屬性時,會自動調用類中定義的__get()# __call 和 __callStatic 對不存在或者權限不夠的類方法進行接管
$obj->getUserInfo('1000068'); //當調用一個不存在的類方法時,會調用__call(),并自動將當前方法名和參數傳到__call方法中
\Extend\Object::getOpenId('1000068'); //當調用一個不存在的類靜態方法時,會調用__callStatic(),并自動將當前方法名和參數傳遞到__callStatic方法中# echo或print對象時,由__toString 接管
echo $obj; //當echo或print一個對象時,會自動調用類中定義的__toString方法# 在php中,如果我們把一個對象當成函數用,則由__invoke()接管
$obj('xiaobudiu');//當我們將一個對象當成函數用的時候,會自動調用當前類中定義的__invoke()方法
2、 Extend/Object.php
<?php
namespace Extend;
/*** 要求類名必須和文件名保持一致,即類名是Object,則所在文件名為Object.php* Class Object* @package Extend*/
class Object
{protected $array = array();/*** 在代碼要給未定義的屬性賦值時調用,或在類外部修改被private修飾的類屬性時被調用*/function __set($name, $value){echo "this is __set func";}/*** 當在類外部訪問被private或proteced修飾的屬性或訪問一個類中原本不存在的屬性時被調用* @param $name*/function __get($name){echo "this is __get func";}/*** 當試圖調用不存在的方法或權限不足時會觸發__call()* @param $name 調用不存在的類方法時那個不存在的類方法的方法名* @param $arguments 調用不存在的類方法時傳遞的參數*/function __call($name, $arguments){var_dump($name,$arguments);}/*** 當試圖調用不存在的靜態方法或權限不足時會觸發__callStatic()* @param $name 調用不存在的靜態方法時那個不存在的方法的方法名* @param $arguments 調用不存在的靜態方法時傳遞的參數*/function __callStatic($name,$arguments){var_dump($name,$arguments);}/*** 當使用echo或print打印對象時會調用__toString()方法將對象轉化為字符串*/function __toString(){echo "this is __toString func";}/*** 對象本身不能直接當函數用,如果被當做函數用,會直接回調__invoke方法* @param $param*/function __invoke($param){echo $param."<br>this is __invoke func";}}
?
?
四、三種基礎設計模式
1、工廠模式
通過傳入參數的不同,來實例化不同的類。
index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載//構造實例化緩存類時傳入的參數
$config = array('host' => '127.0.0.1','pass' => 'myRedis&&&'
);
//工廠模式創建cache對象
$cache = Extend\CacheFactory::getCacheObj('redis',$config);
var_dump($cache);
Extend/CacheFactory.php
<?php
namespace Extend;class CacheFactory
{const FILE = 1;const MEMCACHE = 2;const REDIS = 3;static $instance;//定義靜態屬性,用于存儲對象/*** 工廠類創建緩存對象* @param $type 指定緩存類型* @param array $options 傳入緩存參數* @return FileCache|Memcache|RedisCache*/static function getCacheObj($type, array $options){switch ($type) {case 'file':case self::FILE:self::$instance = new FileCache($options);break;case 'memcache':case self::MEMCACHE:self::$instance = new Memcache($options);break;case 'redis':case self::REDIS:self::$instance = new RedisCache($options);break;default:self::$instance = new FileCache($options);break;}return self::$instance;}
}
?
2、單例模式
保證一個類只實例化一個類對象,進而減少系統開銷和資源的浪費
index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載//單例模式創建對象
$obj = Extend\SingleObject::getInstance();
$obj2 = Extend\SingleObject::getInstance();
var_dump($obj,$obj2);//從結果可以看出,兩個實例化的對象其實是一個對象
Extend/SingleObject.php
<?php
namespace Extend;/*** 單例模式創建唯一類對象* Class SingleObject* @package Extend*/
class SingleObject
{//私有的靜態屬性,用于存儲類對象private static $instance = null;//私有的構造方法,保證不允許在類外 newprivate function __construct(){}//私有的克隆方法, 確保不允許通過在類外 clone 來創建新對象private function __clone(){}//公有的靜態方法,用來實例化唯一當前類對象public static function getInstance(){if(is_null(self::$instance)){self::$instance = new self;}return self::$instance;}}
?
3、注冊樹模式
將我們用到的對象注冊到注冊樹上,然后在之后要用到這個對象的時候,直接從注冊樹上取下來就好。(就和我們用全局變量一樣方便)
Extend/RegisterTree,php
<?php
namespace Extend;/*** 注冊樹模式* Class RegisterTree* @package Extend*/
class RegisterTree
{static protected $objects;//靜態類屬性,用于儲存注冊到注冊樹上的對象/*** 將對象注冊到注冊樹上* @param $alias 對象的別名* @param $object 對象*/static function setObject($alias,$object){self::$objects[$alias] = $object;}/*** 從注冊樹上取出給定別名相應的對象* @param $alias 將對象插入到注冊樹上時寫的別名* @return mixed 對象*/static protected function getObject($alias){return self::$objects[$alias];}/*** 將對象從注冊樹上刪除* @param $alias 將對象插入到注冊樹上時寫的別名*/public function unsetObject($alias){unset(self::$objects[$alias]);}}
關于注冊樹模式,這里推薦一篇文章 ,可以方便理解。?https://www.cnblogs.com/DeanChopper/p/4767181.html
?
?
五、其他常見的8種PHP設計模式
1、適配器模式
將一個類的接口轉換成客戶希望的另一個接口,適配器模式使得原本的由于接口不兼容而不能一起工作的那些類可以一起工作。
應用場景:老代碼接口不適應新的接口需求,或者代碼很多很亂不便于繼續修改,或者使用第三方類庫。
常見的有兩種適配器,分別是類適配器和對象適配器,這里拿更看好的對象適配器舉例:
<?php
namespace Extend;/*** 對象適配器模式具體流程* 1、根據需求定義接口,進而滿足新需求功能* 2、定義新類,繼承并實現定義的接口* 3、在實現接口時,原有的功能,只通過原有類對象調用原有類功能(委托)* 4、再根據需求,在新類中實現新需求功能* 【適用性】* (1)你想使用一個已經存在的類,而它的接口不符合你的需求* (2)你想創建一個可以復用的類,該類可以與其他不相關的類或不可預見的類協同工作* (3)你想使用一個已經存在的子類,但是不可能對每一個都進行子類化以匹配它們的接口。對象適配器可以適配它的父類接口(僅限于對*//*** 目標角色(根據需求定義含有舊功能加上新功能的接口)* Interface Target 我們期望得到的功能類* @package Extend*/
interface Target
{public function simpleMethod1();public function simpleMethod2();
}/*** 源角色(在新功能提出之前的舊功能類和方法)* Class Adaptee* @package Extend*/
class Adaptee
{public function simpleMethod1(){echo 'Adapter simpleMethod1'."<br>";}}/*** 類適配器角色(新定義接口的具體實現)* Class Adapter* @package Extend*/
class Adapter implements Target
{private $adaptee;function __construct(){//適配器初始化直接new 原功能類,以方便之后委派$adaptee = new Adaptee();$this->adaptee = $adaptee;}//委派調用Adaptee的sampleMethod1方法public function simpleMethod1(){echo $this->adaptee->simpleMethod1();}public function simpleMethod2(){echo 'Adapter simpleMethod2'."<br>";}}/*** 客戶端調用*/
$adapter = new Adapter();
$adapter->simpleMethod1();
$adapter->simpleMethod2();
這篇文章介紹了類適配器的使用,感興趣的可以了解一下?https://blog.csdn.net/wzllai/article/details/7832815
?
?
2、策略模式
將一組特定的行為和算法封裝成類,以適應某些特定的上下文環境,這種模式就是策略模式,策略模式可以實現依賴倒置以及控制反轉。
實例舉例:假如一個電商網站系統,針對男性女性用戶要各自跳轉到不同的商品類目,并且所有的廣告位展示展示不同的廣告。
index.php
<?php/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載/*** 首頁數據控制器* Class Index*/
class Home
{/*** 最好寫上這個注釋,告訴phpstorm是對應的哪個接口類,否則雖然程序執行正確,但phpstorm識別不了* @var \Extend\UserType*/protected $userType;/*** 首頁展示數據* 使用策略模式* Index constructor.*/function index(){echo "AD:";$this->userType->showAd();echo "Category:";$this->userType->showCategory();}/*** 策略模式* 根據傳遞的用戶性別展示不同類別數據* @param \Extend\UserType $userType*/function setUserType(\Extend\UserType $userType){$this->userType = $userType;}}$obj = new Home();
if ($_GET['userType'] == 'female'){$userType = new \Extend\FemaleUserType();
} else {$userType = new \Extend\MaleUserType();
}
$obj->setUserType($userType);
$obj->index();
Extend/userType.php(定義的接口)
<?phpnamespace Extend;/*** 策略模式* 定義根據性別不同展示不同商品類目和廣告接口* Interface UserType* @package Extend*/
interface UserType
{//顯示廣告function showAd();//展示類目function showCategory();}
MaleUserType.php、FemaleUserType.php(具體實現的類?)
<?phpnamespace Extend;/*** 定義男性商品類目和廣告位數據接口* Class MaleUserType* @package Extend*/
class MaleUserType implements UserType
{/*** 廣告欄數據展示*/function showAd(){echo "this is 男性’s 廣告條目數據";}/*** 商品類目數據展示*/function showCategory(){echo "this is 男性’s 商品類目數據";}}
<?phpnamespace Extend;/*** 定義女性商品類目和廣告位數據接口* Class FemaleUserType* @package Extend*/
class FemaleUserType implements UserType
{/*** 廣告欄數據展示*/function showAd(){echo "this is 女性’s 廣告條目數據";}/*** 商品類目數據展示*/function showCategory(){echo "this is 女性’s 商品類目數據";}}
顯示效果:
?
?
3、數據對象映射模式
將對象和數據存儲映射起來,對一個對象的操作會映射為對數據存儲的操作。
下面在代碼中實現數據對象映射模式,我們將實現一個ORM類,將復雜的sql語句映射成對象屬性的操作。并結合使用數據對象映射模式、工廠模式、注冊模式。
?
-----(1)數據庫映射模式簡單實例實現
index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載//使用數據對象映射模式代替寫sql
$user = new Extend\User(25);
$user->name = '小卜丟飯團子';
$user->salary = '20000';
$user->city = '浙江省';
Extend/User.php
<?phpnamespace Extend;class User
{//對應數據庫中的4個字段public $id;public $name;public $salary;public $city;//存儲數據庫連接對象屬性protected $pdo;public $data;function __construct($id){$this->id = $id;$this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456');}function __destruct(){$this->pdo->query("update user set name = '{$this->name}',salary = '{$this->salary}',city = '{$this->city}' where id='{$this->id}'");}
}
?這樣,執行index.php文件,數據庫就會發生相應的操作,也就實現了基本的數據對象映射。
?
-------(2)數據庫映射模式復雜案例實現
index.php
<?php/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載class EX
{function index(){//使用數據對象映射模式代替寫sql$user = Extend\Factory::getUserObj(25);$user->name = '小卜丟飯團子';$user->salary = '20000';$user->city = '浙江省';}function test(){$user = Extend\Factory::getUserObj(25);$user->city = '廣東省';}}$ex = new EX();
$ex->index();
Extend/Factory.php
<?phpnamespace Extend;class Factory
{/*** 工廠模式創建數據庫對象,單例模式保證創建唯一db對象* @return mixed*/static function CreateDatabaseObj(){$db = Database::getInstance();return $db;}/*** 工廠模式創建user對象,注冊樹模式保證創建唯一對象,避免資源浪費* @param $id* @return User|mixed*/static function getUserObj($id){$key = 'user'.$id;$user = RegisterTree::getObject($key);if (!$user) {$user = new User($id);RegisterTree::setObject($key,$user);}return $user;}
}
Extend/Register.php
<?phpnamespace Extend;/*** 注冊樹模式* Class RegisterTree* @package Extend*/
class RegisterTree
{static protected $objects;//靜態類屬性,用于儲存注冊到注冊樹上的對象/*** 將對象注冊到注冊樹上* @param $alias 對象的別名* @param $object 對象*/static function setObject($alias,$object){self::$objects[$alias] = $object;}/*** 從注冊樹上取出給定別名相應的對象* @param $alias 將對象插入到注冊樹上時寫的別名* @return mixed 對象*/static function getObject($alias){return self::$objects[$alias];}/*** 將對象從注冊樹上刪除* @param $alias 將對象插入到注冊樹上時寫的別名*/public function unsetObject($alias){unset(self::$objects[$alias]);}}
Extend/User.php
<?phpnamespace Extend;class User
{//對應數據庫中的4個字段public $id;public $name;public $salary;public $city;//存儲數據庫連接對象屬性protected $pdo;public $data;function __construct($id){$this->id = $id;$this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456');}function __destruct(){$this->pdo->query("update user set name = '{$this->name}',salary = '{$this->salary}',city = '{$this->city}' where id='{$this->id}'");}
}
這樣,就實現了稍復雜的數據對象映射模式和工廠模式、注冊樹模式相結合的案例。
?
?
4、觀察者模式
當一個對象狀態發生改變時,依賴它的對象會全部收到通知,并自動更新。
場景:一個事件發生后,要執行一連串更新操作。傳統的編程方式就是在事件的代碼之后直接加入處理邏輯,當更新的邏輯增多之后,代碼會變的難以維護。這種方式是耦合的,侵入式的,增加新的邏輯需要修改事件主體的代碼。觀察者模式實現了低耦合,非侵入式的通知與更新機制。
?
4.1、傳統模式舉例:
<?php/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載/*** 一個事件的邏輯控制器* Class Event*/
class Event
{/*** 用戶確認訂單*/function firmOrder(){//這里假設一個事件發生了,比如用戶已經完成下單echo "用戶已下單<br>";//傳統方式是在發生一個事件之后直接進行一系列的相關處理,耦合度比較高,比如寫入日志,給用戶發郵件等等echo "在用戶下單之后進行的一系列操作<br>";}}$event = new Event();
$event->firmOrder();
?
4.2、觀察者模式典型實現方式:
(1)定義2個接口:觀察者(通知)接口、被觀察者(主題)接口
(2)定義2個類,觀察者類實現觀察者接口、被觀察者類實現被觀察者接口
(3)被觀察者注冊自己需要通知的觀察者
(4)被觀察者類某個業務邏輯發生時,通知觀察者對象,進而每個觀察者執行自己的業務邏輯。
?
代碼示例:
test.php
<?php
/*** 觀察者模式場景描述:* 1、購票后記錄文本日志* 2、購票后記錄數據庫日志* 3、購票后發送短信* 4、購票送抵扣卷、兌換卷、積分* 5、其他各類活動等*//*** 觀察者接口*/
interface TicketObserver
{function buyTicketOver($sender, $args); //得到通知后調用的方法
}/*** 被觀察者接口(購票主題接口)*/
interface TicketObserved
{function addObserver($observer); //提供注冊觀察者方法
}/*** 主體邏輯,繼承被觀察者接口* Class BuyTicket*/
class BuyTicket implements TicketObserved
{/*** 定義觀察者數組屬性,用于儲存觀察者* @var array*/private $observers = array();/*** 實現被觀察者接口定義的方法(添加觀察者)* @param $observer 實例化后的觀察者對象*/public function addObserver($observer){$this->observers[] = $observer;}/*** 購票主體方法* BuyTicket constructor.* @param $ticket 購票排號*/public function buyTicket($ticket){//1、根據需求寫購票邏輯//..............//2、購票成功之后,循環通知觀察者,并調用其buyTicketOver實現不同業務邏輯foreach ($this->observers as $observe) {$observe->buyTicketOver($this, $ticket); //$this 可用來獲取主題類句柄,在通知中使用}}}/*** 購票成功后,發送短信通知* Class buyTicketMSN*/
class buyTicketMSN implements TicketObserver
{public function buyTicketOver($sender, $ticket){echo (date ( 'Y-m-d H:i:s' ) . " 短信日志記錄:購票成功:$ticket<br>");}
}/*** 購票成功后,記錄日志* Class buyTicketLog*/
class buyTicketLog implements TicketObserver
{public function buyTicketOver($sender, $ticket) {echo (date ( 'Y-m-d H:i:s' ) . " 文本日志記錄:購票成功:$ticket<br>");}
}/*** 購票成功后,贈送優惠券* Class buyTicketCoupon*/
class buyTicketCoupon implements TicketObserver
{public function buyTicketOver($sender, $ticket) {echo (date ( 'Y-m-d H:i:s' ) . " 贈送優惠券:購票成功:$ticket 贈送10元優惠券1張。<br>");}
}//實例化購票類
$buy = new BuyTicket();
//添加多個觀察者
$buy->addObserver(new buyTicketMSN());
$buy->addObserver(new buyTicketLog());
$buy->addObserver(new buyTicketCoupon());
//開始購票
$buy->buyTicket ("7排8號");
?瀏覽器顯示結果:
?
5、原型模式
原型模式與工廠模式的作用類似,都是用來創建對象的。但是實現方式是不同的。原型模式是先創建好一個原型對象,然后通過clone原型對象來創建新的對象。這樣,就免去了類創建時重復的初始化操作。
原型模式適用于大對象的創建,創建一個大對象需要很大的開銷,如果每次new就會消耗很大,原型模式僅需內存拷貝即可。
代碼實例:
<?php
/*** 抽象原型角色*/
interface Prototype
{public function copy();
}/*** 具體原型角色*/
class ConcretePrototype implements Prototype
{private $_name;public function __construct($name){$this->_name = $name;}public function setName($name){$this->_name = $name;}public function getName(){return $this->_name;}public function copy(){//深拷貝實現//$serialize_obj = serialize($this); // 序列化//$clone_obj = unserialize($serialize_obj); // 反序列化//return $clone_obj;// 淺拷貝實現return clone $this;}}/*** 測試深拷貝用的引用類*/
class Demo
{public $array;
}//測試
$demo = new Demo();
$demo->array = array(1, 2);
$object1 = new ConcretePrototype($demo);
$object2 = $object1->copy();var_dump($object1->getName());
echo '<br />';
var_dump($object2->getName());
echo '<br />';$demo->array = array(3, 4);
var_dump($object1->getName());
echo '<br />';
var_dump($object2->getName());
echo '<br />';
?瀏覽器顯示結果:
關于原型模式文章:https://www.jb51.net/article/75982.html
?
6、裝飾器模式
可以動態的添加或修改類的功能
一個類實現一個功能,如果要再修改或添加額外的功能,傳統的編程模式需要寫一個子類繼承它,并重新實現類的方法。
使用裝飾器模式,僅需在運行時添加一個裝飾器對象即可實現,可以實現最大的靈活性。
<?php
/*** 裝飾器流程* 1、聲明裝飾器接口(裝飾器接口)* 2、具體類繼承并實現裝飾器接口(顏色裝飾器實現,字體大小裝飾器實現)* 3、在被裝飾者類中定義"添加裝飾器"方法(EchoText類中的addDecorator方法)* 4、在被裝飾者類中定義調用裝飾器的方法(EchoText類中的beforeEcho和afterEcho方法)* 5、使用時,實例化被裝飾者類,并傳入裝飾器對象(比如new ColorDecorator('yellow'))*//*** 裝飾器接口* Class Decorator*/
interface Decorator
{public function beforeEcho();public function afterEcho();
}/*** 顏色裝飾器實現* Class ColorDecorator*/
class ColorDecorator implements Decorator
{protected $color;public function __construct($color){$this->color = $color;}public function beforeEcho(){echo "<dis style='color: {$this->color}'>";}public function afterEcho(){echo "</div>";}
}/*** 字體大小裝飾器實現* Class SizeDecorator*/
class SizeDecorator implements Decorator
{protected $size;public function __construct($size){$this->size = $size;}public function beforeEcho(){echo "<dis style='font-size: {$this->size}px'>";}public function afterEcho(){echo "</div>";}
}/*** 被裝飾者* 輸出一個字符串* 裝飾器動態添加功能* Class EchoText*/
class EchoText
{protected $decorators = array();//存放裝飾器//裝飾方法public function Index(){//調用裝飾器前置操作$this->beforeEcho();echo "你好,我是裝飾器。";//調用裝飾器后置操作$this->afterEcho();}//添加裝飾器public function addDecorator(Decorator $decorator){$this->decorators[] = $decorator;}//執行裝飾器前置操作 先進先出原則protected function beforeEcho(){foreach ($this->decorators as $decorator)$decorator->beforeEcho();}//執行裝飾器后置操作 先進后出原則protected function afterEcho(){$tmp = array_reverse($this->decorators);foreach ($tmp as $decorator)$decorator->afterEcho();}
}//實例化輸出類
$echo = new EchoText();
//增加裝飾器
$echo->addDecorator(new ColorDecorator('yellow'));
//增加裝飾器
$echo->addDecorator(new SizeDecorator('22'));
//輸出
$echo->Index();
?
7、迭代器模式
在不需要了解內部實現的前提下,遍歷一個聚合對象的內部元素而又不暴露該對象的內部表示,這就是PHP迭代器模式的定義。
相對于傳統編程模式,迭代器模式可以隱藏遍歷元素的所需的操作。
index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載$users = new Extend\AllUser();
//循環遍歷出所有用戶數據
foreach ($users as $user) {var_dump($user);
}
Extend/AllUser.php
<?php
namespace Extend;/*** 迭代器模式,繼承php內部自帶的迭代器接口(\Iterator)* Class AllUser* @package Extend*/
class AllUser implements \Iterator
{protected $index = 0;//表示索引protected $ids = array();//用于儲存所有user的id(實際應用中,可以采用注冊樹模式進行存儲)protected $pdo;//用于存儲數據庫對象function __construct(){//獲取pdo數據庫對象$this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456');//獲取所有用戶的id$this->ids = $this->pdo->query("select id from user")->fetchAll(2);}/*** 實現接口方法,重置迭代器,回到集合開頭*/public function rewind(){$this->index = 0;}/*** 實現接口方法,獲取當前元素* @return mixed|void*/public function current(){$id = $this->ids[$this->index]['id'];//獲取當前用戶的數據$user_data = $this->pdo->query("select * from user where id='{$id}'")->fetch(2);return $user_data;}/*** 實現接口方法,獲取當前元素鍵值* @return mixed|void*/public function key(){return $this->index;}/*** 實現接口方法,獲取下一個元素*/public function next(){$this->index++;}/*** 實現接口方法,驗證是否還有下一個元素* @return bool|void*/public function valid(){return $this->index < count($this->ids);}}
關于php迭代器文章?http://laravelacademy.org/post/2882.html
?
8、代理模式
在客戶端與實體之間建立一個代理對象(proxy),客戶端對實體進行操作全部委派給代理對象,隱藏實體的具體實現細節。
典型的應用就是mysql的主從結構,讀寫分離。在mysql中,對所有讀的操作請求從庫,所有寫的操作請求主庫。
聲明一個代理類,前臺使用時只需創建一個代理類,調用對應方法即可。代碼實例:
index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載// 1、傳統編程模式是手動選擇
#查詢操作使用從庫
//$db_slave = Extend\Factory::getDatabase('slave');
//$info = $db_slave->query("select * from user where id = 1 limit 1");
#增刪改操作使用主庫
//$db_master = Extend\Factory::getDatabase('master');
//$db_master->query("update user name = 'xiaobudiu' where id = 29 limit 1");// 2、使用代理模式
$db_proxy = new Extend\Proxy();
$db_proxy->getUserName(1);
$db_proxy->setUserName(29,'xiaobudiu');
?Extend/Proxy.php
<?php
namespace Extend;class Proxy implements IUserProxy
{function getUserName($id){$db = Factory::getDatabase('slave');$db->query("select name from user where id =$id limit 1");}function setUserName($id, $name){$db = Factory::getDatabase('master');$db->query("update user set name = $name where id =$id limit 1");}
}
Extend/Factory.php
<?php
namespace Extend;class Factory
{static function getDatabase($id){$key = 'database_'.$id;if ($id == 'slave'){$slaves = Application::getInstance()->config['database']['slave'];$db_conf = $slaves[array_rand($slaves)];} else {$db_conf = Application::getInstance()->config['database'][$id];}//注冊樹模式存儲及獲取對象$db = Register::get($key);if (!$db) {$db = new Database\MySQLi();$db->connect($db_conf['host'], $db_conf['user'], $db_conf['password'], $db_conf['dbname']);Register::set($key, $db);}return $db;}}
?Extend/Application.php
<?php
namespace Extend;class Application
{public $base_dir;protected static $instance;public $config;protected function __construct($base_dir){$this->base_dir = $base_dir;$this->config = new Config($base_dir.'/configs');}static function getInstance($base_dir = ''){if (empty(self::$instance)){self::$instance = new self($base_dir);}return self::$instance;}}
?Extend/Config.php
<?php
namespace Extend;/*** 配置類,繼承于php自帶的ArrayAccess接口* 允許一個對象以數組的方式訪問* Class Config* @package Extend*/
class Config implements \ArrayAccess
{protected $path;protected $configs = array();function __construct($path){$this->path = $path;}function offsetGet($key){if (empty($this->configs[$key])){$file_path = $this->path.'/'.$key.'.php';$config = require $file_path;$this->configs[$key] = $config;}return $this->configs[$key];}function offsetSet($key, $value){throw new \Exception("cannot write config file.");}function offsetExists($key){return isset($this->configs[$key]);}function offsetUnset($key){unset($this->configs[$key]);}
}
configs/database.php
<?php
$config = array('master' => array('type' => 'MySQL','host' => '127.0.0.1','user' => 'root','password' => '123456','dbname' => 'test',),'slave' => array('slave1' => array('type' => 'MySQL','host' => '127.0.0.1','user' => 'root','password' => '123456','dbname' => 'test',),'slave2' => array('type' => 'MySQL','host' => '127.0.0.1','user' => 'root','password' => '123456','dbname' => 'test',),),
);
return $config;
關于php代理模式文章?https://www.jb51.net/article/27478.htm
?
五、其余設計模式以及總結
文章鏈接:?https://blog.csdn.net/ITYang_/article/details/53366750
?
六、面向對象編程的基本原則
1、單一職責原則:一個類只需要做好一件事情。不要使用一個類完成很多功能,而應該拆分成更多更小的類。
2、開放封閉原則:一個類寫好之后,應該是可擴展而不可修改的。
3、依賴倒置原則:一個類不應該強依賴另外一個類,每個類對于另外一個類都是可替換的。
4、配置化原則:盡量使用配置,而不是硬編碼。
5、面向接口編程原則:只需要關心某個類提供了哪些接口,而不需要關心他的實現。
?
七、自動加載配置類文件
1、php中使用ArrayAccess實現配置文件的加載(使得程序可以以數組的方式進行讀取配置)
(1)定義Config.php,繼承php自帶的ArrayAccess接口,并實現相應的方法,用于讀取和設置配置
Extend/Config.php
<?php
namespace Extend;/*** 配置類,繼承于php自帶的ArrayAccess接口* 允許一個對象以數組的方式訪問* Class Config* @package Extend*/
class Config implements \ArrayAccess
{protected $path;protected $configs = array();function __construct($path){$this->path = $path;}function offsetGet($key){if (empty($this->configs[$key])){$file_path = $this->path.'/'.$key.'.php';$config = require $file_path;$this->configs[$key] = $config;}return $this->configs[$key];}function offsetSet($key, $value){throw new \Exception("cannot write config file.");}function offsetExists($key){return isset($this->configs[$key]);}function offsetUnset($key){unset($this->configs[$key]);}
}
(2)configs/database.php
<?php
$config = array('master' => array('type' => 'MySQL','host' => '127.0.0.1','user' => 'root','password' => '123456','dbname' => 'test',),'slave' => array('slave1' => array('type' => 'MySQL','host' => '127.0.0.1','user' => 'root','password' => '123456','dbname' => 'test',),'slave2' => array('type' => 'MySQL','host' => '127.0.0.1','user' => 'root','password' => '123456','dbname' => 'test',),),
);
return $config;
?(3)讀取配置
index.php
<?php
/*** 框架入口文件*/
define('BASEDIR',__DIR__);//項目根目錄
include BASEDIR.'/Extend/Loader.php';//引入項目自動加載類文件
spl_autoload_register('\\Extend\\Loader::autoload');//執行自動加載函數,完成類的自動加載$config = new Extend\Config(__DIR__.'/configs');
var_dump($config['database']);
(4)瀏覽器顯示:
?
到此,就可以在程序中隨心所欲的加載配置文件了。
?
2、在工廠方法中讀取配置,生成可配置化的對象
Extend/Factory.php
<?php
namespace Extend;class Factory
{static function getDatabase($id){$key = 'database_'.$id;if ($id == 'slave'){$slaves = Application::getInstance()->config['database']['slave'];$db_conf = $slaves[array_rand($slaves)];} else {$db_conf = Application::getInstance()->config['database'][$id];}//注冊樹模式存儲及獲取對象$db = Register::get($key);if (!$db) {$db = new Database\MySQLi();$db->connect($db_conf['host'], $db_conf['user'], $db_conf['password'], $db_conf['dbname']);Register::set($key, $db);}return $db;}}
?Extend/Application.php
<?php
namespace Extend;class Application
{public $base_dir;protected static $instance;public $config;protected function __construct($base_dir){$this->base_dir = $base_dir;$this->config = new Config($base_dir.'/configs');}static function getInstance($base_dir = ''){if (empty(self::$instance)){self::$instance = new self($base_dir);}return self::$instance;}}
?Extend/Config.php
<?php
namespace Extend;/*** 配置類,繼承于php自帶的ArrayAccess接口* 允許一個對象以數組的方式訪問* Class Config* @package Extend*/
class Config implements \ArrayAccess
{protected $path;protected $configs = array();function __construct($path){$this->path = $path;}function offsetGet($key){if (empty($this->configs[$key])){$file_path = $this->path.'/'.$key.'.php';$config = require $file_path;$this->configs[$key] = $config;}return $this->configs[$key];}function offsetSet($key, $value){throw new \Exception("cannot write config file.");}function offsetExists($key){return isset($this->configs[$key]);}function offsetUnset($key){unset($this->configs[$key]);}
}
?