我們分三篇文章來總結一下設計模式在PHP中的應用,這是第一篇創建型模式。
一、設計模式簡介:
首先我們來認識一下什么是設計模式:
設計模式是一套被反復使用、容易被他人理解的、可靠的代碼設計經驗的總結。
設計模式不是Java的專利,我們用面向對象的方法在PHP里也能很好的使用23種設計模式。
那么我們常說的架構、框架和設計模式有什么關系呢?
架構是一套體系結構,是項目的整體解決方案;框架是可供復用的半成品軟件,是具體程序代碼。架構一般會涉及到采用什么樣的框架來加速和優化某部分問題的解決,而好的框架代碼里合理使用了很多設計模式。
二、提煉設計模式的幾個原則:
開閉原則:模塊應對擴展開放,而對修改關閉。
里氏代換原則:如果調用的是父類的話,那么換成子類也完全可以運行。
依賴倒轉原則:抽象不依賴細節,面向接口編程,傳遞參數盡量引用層次高的類。
接口隔離原則:每一個接口只負責一種角色。
合成/聚合復用原則:要盡量使用合成/聚合,不要濫用繼承。
三、設計模式的功用:
1、設計模式能解決
替換雜亂無章的代碼,形成良好的代碼風格
代碼易讀,工程師們都能很容易理解
增加新功能時不用修改接口,可擴展性強
穩定性好,一般不會出現未知的問題
2、設計模式不能解決
設計模式是用來組織你的代碼的模板,而不是直接調用的庫;
設計模式并非最高效,但是代碼的可讀性和可維護性更重要;
不要一味追求并套用設計模式,重構時多考慮;
四、設計模式分類
1、創建型模式:
單例模式、工廠模式(簡單工廠、工廠方法、抽象工廠)、創建者模式、原型模式。
2、結構型模式:
適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
3、行為型模式:
模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式。
五、創建型設計模式
1、單例模式
目的:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
應用場景:數據庫連接、緩存操作、分布式存儲。
<?php
/**
* 優才網公開課示例代碼
*
* 單例模式
*
* @author 優才網全棧工程師教研組
* @see http://www.ucai.cn
*/
classDbConn
{
private static $_instance = null;
protected static $_counter = 0;
protected $_db;
//私有化構造函數,不允許外部創建實例
private function __construct()
{
self::$_counter += 1;
}
public function getInstance()
{
if (self::$_instance == null)
{
self::$_instance = newDbConn();
}
return self::$_instance;
}
public function 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
*/
//抽象產品
interfacePerson {
public function getName();
}
//具體產品實現
classTeacher implements Person {
function getName() {
return "老師\n";
}
}
classStudent implements Person {
function getName() {
return "學生\n";
}
}
//簡單工廠
classSimpleFactory {
public static function getPerson($type) {
$person = null;
if ($type == 'teacher') {
$person = new Teacher();
} elseif ($type == 'student') {
$person = new Student();
}
return $person;
}
}
//簡單工廠調用
classSimpleClient {
function main() {
// 如果不用工廠模式,則需要提前指定具體類
// $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();
}
}
//工廠方法
interfaceCommFactory {
public function getPerson();
}
//具體工廠實現
classStudentFactory implements CommFactory {
function getPerson(){
return new Student();
}
}
classTeacherFactory implements CommFactory {
function getPerson() {
return new Teacher();
}
}
//工廠方法調用
classCommClient {
static function main() {
$factory= new TeacherFactory();
echo$factory->getPerson()->getName();
$factory= new StudentFactory();
echo$factory->getPerson()->getName();
}
}
//抽象工廠模式另一條產品線
interfaceGrade {
function getYear();
}
//另一條產品線的具體產品
classGrade1 implements Grade {
public function getYear() {
return '2003級';
}
}
classGrade2 implements Grade {
public function getYear() {
return '2004級';
}
}
//抽象工廠
interfaceAbstractFactory {
function getPerson();
function getGrade();
}
//具體工廠可以產生每個產品線的產品
classGrade1TeacherFactory implements AbstractFactory {
public function getPerson() {
return new Teacher();
}
public function getGrade() {
return new Grade1();
}
}
classGrade1StudentFactory implements AbstractFactory {
public function getPerson() {
return new Student();
}
public function getGrade() {
return new Grade1();
}
}
classGrade2TeacherFactory implements AbstractFactory {
public function getPerson() {
return new Teacher();
}
public function getGrade() {
return new Grade2();
}
}
//抽象工廠調用
classFactoryClient {
function printInfo($factory) {
echo$factory->getGrade()->getYear().$factory->getPerson()->getName();
}
function main() {
$client = new FactoryClient();
$factory = newGrade1TeacherFactory();
$client->printInfo($factory);
$factory = newGrade1StudentFactory();
$client->printInfo($factory);
$factory = newGrade2TeacherFactory();
$client->printInfo($factory);
}
}
//簡單工廠
//SimpleClient::main();
//工廠方法
//CommClient::main();
//抽象工廠
FactoryClient::main();
?>三種工廠的區別是,抽象工廠由多條產品線,而工廠方法只有一條產品線,是抽象工廠的簡化。而工廠方法和簡單工廠相對,大家初看起來好像工廠方法增加了許多代碼但是實現的功能和簡單工廠一樣。但本質是,簡單工廠并未嚴格遵循設計模式的開閉原則,當需要增加新產品時也需要修改工廠代碼。但是工廠方法則嚴格遵守開閉原則,模式只負責抽象工廠接口,具體工廠交給客戶去擴展。在分工時,核心工程師負責抽象工廠和抽象產品的定義,業務工程師負責具體工廠和具體產品的實現。只要抽象層設計的好,框架就是非常穩定的。
3、創建者模式
在創建者模式中,客戶端不再負責對象的創建與組裝,而是把這個對象創建的責任交給其具體的創建者類,把組裝的責任交給組裝類,客戶端支付對對象的調用,從而明確了各個類的職責。
應用場景:創建非常復雜,分步驟組裝起來。
<?php
/**
* 優才網公開課示例代碼
*
* 創建者模式
*
* @author 優才網全棧工程師教研組
* @see http://www.ucai.cn
*/
//購物車
classShoppingCart {
//選中的商品
private $_goods = array();
//使用的優惠券
private $_tickets = array();
public function 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;
//我們提供創建者類來封裝購物車的數據組裝
classCardBuilder {
private $_card;
function __construct($card) {
$this->_card = $card;
}
function build($data) {
foreach ($data['goods'] as $goods){
$this->_card->addGoods($goods);
}
foreach ($data['tickets'] as$ticket) {
$this->_card->addTicket($ticket);
}
}
function getCrad() {
return $this->_card;
}
}
$cart =new ShoppingCart();
$builder= new CardBuilder($cart);
$builder->build($data);
echo"after builder:\n";
$cart->printInfo();
?>可以看出,使用創建者模式對內部數據復雜的對象封裝數據組裝過程后,對外接口就會非常簡單和規范,增加修改新數據項也不會對外部造成任何影響。
4、原型模式
用原型實例指定創建對象的種類,并且通過拷貝這個原型來創建新的對象。
應用場景: 類的資源非常多、性能和安全要求,一般和工廠方法結合使用。
<?php
/**
* 優才網公開課示例代碼
*
* 原型模式
*
* @author 優才網全棧工程師教研組
* @see http://www.ucai.cn
*/
//聲明一個克隆自身的接口
interfacePrototype {
function copy();
}
//產品要實現克隆自身的操作
classStudent implements Prototype {
//簡單起見,這里沒有使用getset
public $school;
public $major;
public $name;
public function __construct($school,$major, $name) {
$this->school = $school;
$this->major = $major;
$this->name = $name;
}
public function printInfo() {
printf("%s,%s,%s\n",$this->school, $this->major, $this->name);
}
public function copy() {
returnclone $this;
}
}
$stu1 =new Student('清華大學', '計算機', '張三');
$stu1->printInfo();
$stu2 =$stu1->copy();
$stu2->name= '李四';
$stu2->printInfo();
?>這里可以看到,如果類的成員變量非常多,如果由外部創建多個新對象再一個個賦值,則效率不高代碼冗余也容易出錯,通過原型拷貝復制自身再進行微小修改就是另一個新對象了。
設計模式的第一部分,創建型模式就總結完了。下面還有兩部分結構型設計模式和行為型設計模式稍后繼續。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。