PHP開發自己的框架必備知識點

一、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]);}
}

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282189.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282189.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282189.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux 環境下 jdk1.8 maven3.2.3 Git2.8.0 安裝腳本

2019獨角獸企業重金招聘Python工程師標準>>> # Author: peizhouyu # Date: 2018-09-07 14:24:11 # Last Modified by: peizhouyu # Last Modified time: 2018-09-07 16:02:58#!/bin/bash SOFT_PATH/opt/softif [ ! -d $SOFT_PATH ];then mkdir $SOFT_PATH else …

《嵌入式設備驅動開發精解》——導讀

前言 嵌入式設備驅動開發精解本書的編寫主要是針對從事嵌入式軟件開發人員。本書的內容主要涵蓋ARM CPU以及各種常用外部設備驅動開發的方方面面&#xff0c;包括各種硬件接口、硬件接口協議說明以及各種外設的使用及調試方法&#xff0c;特別是對于開發調試過程中可能遇到的各…

C# WPF通過WindowChrome自定義窗體

概述在WPF界面開發中&#xff0c;系統默認的窗口比較丑&#xff0c;有時候想自定義窗體&#xff0c;比如微信的客戶端窗口這樣&#xff1a;使得左邊的一塊頂到最上端&#xff0c;如下圖所示&#xff1a;這時候我們可以 WindowStyle"None"&#xff0c;AllowsTranspare…

【轉載】遞推公式的特征方程及通項公式

先貼上鏈接&#xff1a;http://blog.csdn.net/happykocola/article/details/73933314 因為最近在復習初賽&#xff0c;然后碰到了這道題&#xff0c;并不會做&#xff0c;才發現有這么高明的方法... 已知遞推關系式&#xff1a; f(n)5f(n-1)-6f(n-2) (n>1) f(0)1 f(1)…

【leetcode】75. Sort Colors

題目如下&#xff1a; 解題思路&#xff1a;我的解題思路是遍歷數組&#xff0c;遇到0刪除該元素并插入到數組頭部&#xff0c;遇到1則不處理&#xff0c;遇到2刪除該元素并插入到數組尾部。 代碼如下&#xff1a; class Solution(object):def sortColors(self, nums):"&q…

每日一言學做人,古之學問,博大精深

前言&#xff1a; 要成為一個有格局&#xff0c;有修養的人&#xff0c;吸納一些有道理的思想和做法&#xff0c;去逐漸提高自己是非常有必要的&#xff0c;有一言&#xff0c;做事先做人&#xff0c;意即于此。因此&#xff0c;每日將自己看到的一段有感的話記錄下來&#xf…

Seal-Report: 開放式數據庫報表工具

Seal Report是.Net的一個基于Apache 2.0 開源工具&#xff0c;完全用C# 語言編寫&#xff0c;最新的6.6 版本采用.NET 6&#xff0c;github: https://github.com/ariacom/Seal-Report。Seal Report提供了一個完整的框架&#xff0c;用于從任何數據庫或任何非SQL源生成每日報告。…

《Ceph源碼分析》——第2章,第2節Buffer

本節書摘來自華章出版社《Ceph源碼分析》一書中的第2章&#xff0c;第2.2節Buffer&#xff0c;作者常濤&#xff0c;更多章節內容可以訪問云棲社區“華章計算機”公眾號查看 2.2 BufferBuffer就是一個命名空間&#xff0c;在這個命名空間下定義了Buffer相關的數據結構, 這些數…

eclipse在server中tomcat server找不到的問題

想要在eclipse的server新建tomcat服務器然而不知道怎么回事找不到Tomcat 7.0 Server 下面的紅圈是tomcat server服務器&#xff08;更新后才出現&#xff09; 網上找的很久&#xff0c;只是找到在eclipse中安裝tomcat插件的方法 Tomcat免安裝版的環境變量配置以及Eclipse下的To…

Sysbench 1.0.15安裝及使用

Sysbench是一款開源的多線程性能測試工具&#xff0c;可以執行CPU/內存/線程/IO/數據庫等方面的性能測試&#xff0c;數據庫目前支持MySQL/Oracle/PostgreSQL。 一、安裝&#xff1a; Github地址&#xff1a;https://github.com/akopytov/sysbench RHEL/CentOS&#xff1a; cur…

PHP根據指定url生成二維碼圖片

一、composer安裝 http://packagist.p2hp.com/packages/codeitnowin/barcode 二、使用 調用generateQrCode()方法即可實現生成二維碼圖片并輸出下載給用戶 <?php namespace manage\Test;use CodeItNow\BarcodeBundle\Utils\QrCode; use common\extensions\Helper; use y…

CA 周記 - 派福利!通過 Azure 零成本進入 CUDA 編程

我們在配置深度學習環境的時候&#xff0c;除了安裝各種庫和框架外&#xff0c;如果需要 GPU 加速&#xff0c;還需要配置 CUDA 。那 CUDA 是什么 &#xff1f;它的作用是什么 &#xff1f;CUDA 編程介紹01什么是 CUDA&#xff1f;CUDA (Compute Unified Device Architecture) …

《視圖更新與關系數據庫理論》——2.1 關系和關系變量

本節書摘來自異步社區出版社《視圖更新與關系數據庫理論》一書中的第2章&#xff0c;第2.1節&#xff0c;作者&#xff1a;【美】C.J. Date&#xff08;達特&#xff09;&#xff0c;更多章節內容可以訪問云棲社區“異步社區”公眾號查看。 2.1 關系和關系變量 每一個關系都有一…

盜取手機敏感信息,Android 6.0之上兼容

盜取手機敏感信息&#xff0c;Android 6.0之上兼容 項目介紹 盜取信息包含&#xff1a; 手機中所有照片手機中所有視頻手機中所有通訊錄手機中所有短信手機中所有通話記錄手機中所有安裝應用兼容Android 6.0及之上版本動態權限申請工具開放效果展示 1.照片信息 MaterialBean{mL…

再記一次Memory Leak分析

性能是優化出來的&#xff0c;不管是在上生產前&#xff0c;還是在上生產后。大部分性能在性能測試階段就能發現問題&#xff0c;但也有一些性能問題&#xff0c;結合生產的環境&#xff0c;生產數據才能表現出來&#xff0c;成為一個顯著的瓶頸。這次是生成pdf造成的內存泄露&…

PHP格式化全國省市區列表

一、代碼部分 /*** 獲取全國省市區列表&#xff08;格式化后&#xff09;*/public function getRegionList(){$data CoreRegion::find()->select([national_code, region_name, parent_id, region_level])->asArray()->all();$data $this->assembleRegionData($…

《C語言開發從入門到精通》一2.4 技術解惑

本節書摘來自異步社區《C語言開發從入門到精通》一書中的第2章&#xff0c;第2.4節&#xff0c;作者王長青 , 韓海玲&#xff0c;更多章節內容可以訪問云棲社區“異步社區”公眾號查看。 2.4 技術解惑 2.4.1 安裝Visual Studio的幾個常見問題 Visual Studio 2010容量巨大&…

POM思想__首頁頁面元素查找、功能點實現進行封裝

一、代碼如下 package www.gui.huohu.pom;import java.util.concurrent.TimeUnit;import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.sele…

061_Apex 異常捕捉

Trigger 中的錯誤處理 在 Trigger 中&#xff0c;我們可以為進行操作的數據進行驗證&#xff0c;類似于驗證規則。如果遇到不符合條件的數據&#xff0c;可以通過 addError() 函數來將錯誤顯示給用戶&#xff0c;并記錄日志。 在如下代碼中&#xff0c;當一個“業務機會”對象被…

從 C# 崩潰異常 中研究 頁堆 布局

一&#xff1a;背景 1.講故事最近遇到一位朋友的程序崩潰&#xff0c;發現崩潰點在富編輯器 msftedit 上&#xff0c;這個不是重點&#xff0c;重點在于發現他已經開啟了 頁堆 &#xff0c;看樣子是做了最后的掙扎。0:000> !analyze -v EXCEPTION_RECORD: (.exr -1) Except…