我們分三篇文章來總結一下設計模式在PHP中的應用,這是第一篇創建型模式。
一、設計模式簡介
首先我們來認識一下什么是設計模式:
設計模式是一套被反復使用、容易被他人理解的、可靠的代碼設計經驗的總結。
設計模式不是Java的專利,我們用面向對象的方法在PHP里也能很好的使用23種設計模式。
那么我們常說的架構、框架和設計模式有什么關系呢?
架構是一套體系結構,是項目的整體解決方案;框架是可供復用的半成品軟件,是具體程序代碼。架構一般會涉及到采用什么樣的框架來加速和優化某部分問題的解決,而好的框架代碼里合理使用了很多設計模式。
二、提煉設計模式的幾個原則:
開閉原則:模塊應對擴展開放,而對修改關閉。
里氏代換原則:如果調用的是父類的話,那么換成子類也完全可以運行。
依賴倒轉原則:抽象不依賴細節,面向接口編程,傳遞參數盡量引用層次高的類。
接口隔離原則:每一個接口只負責一種角色。
合成/聚合復用原則:要盡量使用合成/聚合,不要濫用繼承。
三、設計模式的功用?
設計模式能解決:
替換雜亂無章的代碼,形成良好的代碼風格
代碼易讀,工程師們都能很容易理解
增加新功能時不用修改接口,可擴展性強
穩定性好,一般不會出現未知的問題
設計模式不能解決:
設計模式是用來組織你的代碼的模板,而不是直接調用的庫;
設計模式并非最高效,但是代碼的可讀性和可維護性更重要;
不要一味追求并套用設計模式,重構時多考慮;
四、設計模式分類
1、創建型模式:
單例模式、工廠模式(簡單工廠、工廠方法、抽象工廠)、創建者模式、原型模式。
2、結構型模式:
適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
3、行為型模式:
模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式。
五、創建型設計模式
1、單例模式
目的:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
應用場景:數據庫連接、緩存操作、分布式存儲。
<?php
/**
* 優才網公開課示例代碼
*
* 單例模式
*
*@author 優才網全棧工程師教研組
*@see http://www.ucai.cn
*/
class DbConn
{
privatestatic $_instance = null;
protectedstatic $_counter = 0;
protected$_db;
//私有化構造函數,不允許外部創建實例
privatefunction __construct()
{
self::$_counter+= 1;
}
publicfunction getInstance()
{
if(self::$_instance == null)
{
self::$_instance= new DbConn();
}
returnself::$_instance;
}
publicfunction connect()
{
echo"connected: ".(self::$_counter)."\n";
return$this->_db;
}
}
/*
* 不使用單例模式時,刪除構造函數的private后再測試,第二次調用構造函數后,_counter變成2
*/
// $conn = new DbConn();
// $conn->connect();
// $conn = new DbConn();
// $conn->connect();
//使用單例模式后不能直接new對象,必須調用getInstance獲取
$conn = DbConn::getInstance();
$db = $conn->connect();
//第二次調用是同一個實例,_counter還是1
$conn = DbConn::getInstance();
$db = $conn->connect();
?>
特別說明:這里getInstance里有if判斷然后再生成對象,在多線程語言里是會有并發問題的。例如java的解決方案有二個,給方法加上synchronized關鍵詞變成同步,或者把_instanc的初始化提前放到類成員變量定義時,但是這2種方式php都不支持。不過因為php不支持多線程所以不需要考慮這個問題了。
2、工廠模式
實現:定義一個用于創建對象的接口,讓子類決定實例化哪一個類。
應用場景:眾多子類并且會擴充、創建方法比較復雜。
~~~
<?php
/**
* 優才網公開課示例代碼
*
* 工廠模式
*
*@author 優才網全棧工程師教研組
*@see http://www.ucai.cn
*/
//抽象產品
interface Person {
public function getName();
}
//具體產品實現
class Teacher implements Person {
function getName() {
return "老師\n";
}
}
class Student implements Person {
function getName() {
return "學生\n";
}
}
//簡單工廠
class SimpleFactory {
publicstatic function getPerson($type) {
$person= null;
if($type == 'teacher') {
$person= new Teacher();
}elseif ($type == 'student') {
$person= new Student();
}
return$person;
}
}
//簡單工廠調用
class SimpleClient {
functionmain() {
//如果不用工廠模式,則需要提前指定具體類
//$person = new Teacher();
//echo $person->getName();
//$person = new Student();
//echo $person->getName();
//用工廠模式,則不需要知道對象由什么類產生,交給工廠去決定
$person= SimpleFactory::getPerson('teacher');
echo$person->getName();
$person= SimpleFactory::getPerson('student');
echo$person->getName();
}
}
//工廠方法
interface CommFactory {
public function getPerson();
}
//具體工廠實現
class StudentFactory implements CommFactory{
function getPerson(){
return new Student();
}
}
class TeacherFactory implements CommFactory{
function getPerson() {
return new Teacher();
}
}
//工廠方法調用
class CommClient {
static function main() {
$factory = new TeacherFactory();
echo$factory->getPerson()->getName();
$factory = new StudentFactory();
echo$factory->getPerson()->getName();
}
}
//抽象工廠模式另一條產品線
interface Grade {
functiongetYear();
}
//另一條產品線的具體產品
class Grade1 implements Grade {
publicfunction getYear() {
return'2003級';
}
}
class Grade2 implements Grade {
publicfunction getYear() {
return'2004級';
}
}
//抽象工廠
interface AbstractFactory {
functiongetPerson();
functiongetGrade();
}
//具體工廠可以產生每個產品線的產品
class Grade1TeacherFactory implementsAbstractFactory {
publicfunction getPerson() {
returnnew Teacher();
}
publicfunction getGrade() {
returnnew Grade1();
}
}
class Grade1StudentFactory implementsAbstractFactory {
publicfunction getPerson() {
returnnew Student();
}
publicfunction getGrade() {
returnnew Grade1();
}
}
class Grade2TeacherFactory implementsAbstractFactory {
publicfunction getPerson() {
returnnew Teacher();
}
publicfunction getGrade() {
returnnew Grade2();
}
}
//抽象工廠調用
class FactoryClient {
functionprintInfo($factory) {
echo$factory->getGrade()->getYear().$factory->getPerson()->getName();
}
functionmain() {
$client= new FactoryClient();
$factory= new Grade1TeacherFactory();
$client->printInfo($factory);
$factory= new Grade1StudentFactory();
$client->printInfo($factory);
$factory= new Grade2TeacherFactory();
$client->printInfo($factory);
}
}
//簡單工廠
//SimpleClient::main();
//工廠方法
//CommClient::main();
//抽象工廠
FactoryClient::main();
?>
~~~
三種工廠的區別是,抽象工廠由多條產品線,而工廠方法只有一條產品線,是抽象工廠的簡化。而工廠方法和簡單工廠相對,大家初看起來好像工廠方法增加了許多代碼但是實現的功能和簡單工廠一樣。
但本質是,簡單工廠并未嚴格遵循設計模式的開閉原則,當需要增加新產品時也需要修改工廠代碼。但是工廠方法則嚴格遵守開閉原則,模式只負責抽象工廠接口,具體工廠交給客戶去擴展。在分工時,核心工程師負責抽象工廠和抽象產品的定義,業務工程師負責具體工廠和具體產品的實現。只要抽象層設計的好,框架就是非常穩定的。
3、創建者模式
在創建者模式中,客戶端不再負責對象的創建與組裝,而是把這個對象創建的責任交給其具體的創建者類,把組裝的責任交給組裝類,客戶端支付對對象的調用,從而明確了各個類的職責。
應用場景:創建非常復雜,分步驟組裝起來。
<?php
/**
* 優才網公開課示例代碼
*
* 創建者模式
*
*@author 優才網全棧工程師教研組
*@see http://www.ucai.cn
*/
//購物車
class ShoppingCart {
//選中的商品
private $_goods = array();
//使用的優惠券
private $_tickets = array();
publicfunction addGoods($goods) {
$this->_goods[]= $goods;
}
public function addTicket($ticket) {
$this->_tickets[] = $ticket;
}
public function printInfo() {
printf("goods:%s,tickets:%s\n", implode(',', $this->_goods), implode(',',$this->_tickets));
}
}
//假如我們要還原購物車的東西,比如用戶關閉瀏覽器后再打開時會根據cookie還原
$data = array(
'goods'=> array('衣服', '鞋子'),
'tickets'=> array('減10'),
);
//如果不使用創建者模式,則需要業務類里一步步還原購物車
// $cart = new ShoppingCart();
// foreach ($data['goods'] as $goods) {
// $cart->addGoods($goods);
// }
// foreach ($data['tickets'] as $ticket) {
// $cart->addTicket($ticket);
// }
// $cart->printInfo();
// exit;
//我們提供創建者類來封裝購物車的數據組裝
class CardBuilder {
private$_card;
function__construct($card) {
$this->_card= $card;
}
functionbuild($data) {
foreach($data['goods'] as $goods) {
$this->_card->addGoods($goods);
}
foreach($data['tickets'] as $ticket) {
$this->_card->addTicket($ticket);
}
}
functiongetCrad() {
return$this->_card;
}
}
$cart = new ShoppingCart();
$builder = new CardBuilder($cart);
$builder->build($data);
echo "after builder:\n";
$cart->printInfo();
?>
可以看出,使用創建者模式對內部數據復雜的對象封裝數據組裝過程后,對外接口就會非常簡單和規范,增加修改新數據項也不會對外部造成任何影響。
原型模式
用原型實例指定創建對象的種類,并且通過拷貝這個原型來創建新的對象。
應用場景:類的資源非常多、性能和安全要求,一般和工廠方法結合使用。
~~~
<?php
/**
* 優才網公開課示例代碼
*
* 原型模式
*
*@author 優才網全棧工程師教研組
*@see http://www.ucai.cn
*/
//聲明一個克隆自身的接口
~~~
interface Prototype {
function copy();
}
~~~
~~~
//產品要實現克隆自身的操作
~~~
class Student implements Prototype {
//簡單起見,這里沒有使用getset
public $school;
public $major;
public$name;
publicfunction __construct($school, $major, $name) {
$this->school= $school;
$this->major= $major;
$this->name= $name;
}
publicfunction printInfo() {
printf("%s,%s,%s\n",$this->school, $this->major, $this->name);
}
public function copy() {
return clone $this;
}
}
~~~
$stu1 = new Student('清華大學', '計算機', '張三');
$stu1->printInfo();
$stu2 = $stu1->copy();
$stu2->name = '李四';
$stu2->printInfo();
?>
這里可以看到,如果類的成員變量非常多,如果由外部創建多個新對象再一個個賦值,則效率不高代碼冗余也容易出錯,通過原型拷貝復制自身再進行微小修改就是另一個新對象了。
設計模式的第一部分,創建型模式就總結完了。下面還有兩部分結構型設計模式和行為型設計模式下次繼續分享。
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜