您好,登錄后才能下訂單哦!
我們分三篇文章來(lái)總結(jié)一下設(shè)計(jì)模式在PHP中的應(yīng)用,這是第一篇?jiǎng)?chuàng)建型模式。
一、設(shè)計(jì)模式簡(jiǎn)介:
首先我們來(lái)認(rèn)識(shí)一下什么是設(shè)計(jì)模式:
設(shè)計(jì)模式是一套被反復(fù)使用、容易被他人理解的、可靠的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。
設(shè)計(jì)模式不是Java的專利,我們用面向?qū)ο蟮姆椒ㄔ赑HP里也能很好的使用23種設(shè)計(jì)模式。
那么我們常說的架構(gòu)、框架和設(shè)計(jì)模式有什么關(guān)系呢?
架構(gòu)是一套體系結(jié)構(gòu),是項(xiàng)目的整體解決方案;框架是可供復(fù)用的半成品軟件,是具體程序代碼。架構(gòu)一般會(huì)涉及到采用什么樣的框架來(lái)加速和優(yōu)化某部分問題的解決,而好的框架代碼里合理使用了很多設(shè)計(jì)模式。
二、提煉設(shè)計(jì)模式的幾個(gè)原則:
開閉原則:模塊應(yīng)對(duì)擴(kuò)展開放,而對(duì)修改關(guān)閉。
里氏代換原則:如果調(diào)用的是父類的話,那么換成子類也完全可以運(yùn)行。
依賴倒轉(zhuǎn)原則:抽象不依賴細(xì)節(jié),面向接口編程,傳遞參數(shù)盡量引用層次高的類。
接口隔離原則:每一個(gè)接口只負(fù)責(zé)一種角色。
合成/聚合復(fù)用原則:要盡量使用合成/聚合,不要濫用繼承。
三、設(shè)計(jì)模式的功用:
1、設(shè)計(jì)模式能解決
替換雜亂無(wú)章的代碼,形成良好的代碼風(fēng)格
代碼易讀,工程師們都能很容易理解
增加新功能時(shí)不用修改接口,可擴(kuò)展性強(qiáng)
穩(wěn)定性好,一般不會(huì)出現(xiàn)未知的問題
2、設(shè)計(jì)模式不能解決
設(shè)計(jì)模式是用來(lái)組織你的代碼的模板,而不是直接調(diào)用的庫(kù);
設(shè)計(jì)模式并非最高效,但是代碼的可讀性和可維護(hù)性更重要;
不要一味追求并套用設(shè)計(jì)模式,重構(gòu)時(shí)多考慮;
四、設(shè)計(jì)模式分類
1、創(chuàng)建型模式:
單例模式、工廠模式(簡(jiǎn)單工廠、工廠方法、抽象工廠)、創(chuàng)建者模式、原型模式。
2、結(jié)構(gòu)型模式:
適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
3、行為型模式:
模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態(tài)模式、策略模式、職責(zé)鏈模式、訪問者模式。
五、創(chuàng)建型設(shè)計(jì)模式
1、單例模式
目的:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。
應(yīng)用場(chǎng)景:數(shù)據(jù)庫(kù)連接、緩存操作、分布式存儲(chǔ)。
<?php /** * 優(yōu)才網(wǎng)公開課示例代碼 * * 單例模式 * * @author 優(yōu)才網(wǎng)全棧工程師教研組 * @see http://www.ucai.cn */ classDbConn { private static $_instance = null; protected static $_counter = 0; protected $_db; //私有化構(gòu)造函數(shù),不允許外部創(chuàng)建實(shí)例 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; } }
/* * 不使用單例模式時(shí),刪除構(gòu)造函數(shù)的private后再測(cè)試,第二次調(diào)用構(gòu)造函數(shù)后,_counter變成2 */ // $conn= new DbConn(); //$conn->connect(); // $conn= new DbConn(); //$conn->connect(); //使用單例模式后不能直接new對(duì)象,必須調(diào)用getInstance獲取 $conn =DbConn::getInstance(); $db =$conn->connect(); //第二次調(diào)用是同一個(gè)實(shí)例,_counter還是1 $conn =DbConn::getInstance(); $db =$conn->connect(); ?>
特別說明:這里getInstance里有if判斷然后再生成對(duì)象,在多線程語(yǔ)言里是會(huì)有并發(fā)問題的。例如java的解決方案有二個(gè),給方法加上synchronized關(guān)鍵詞變成同步,或者把_instanc的初始化提前放到類成員變量定義時(shí),但是這2種方式php都不支持。不過因?yàn)閜hp不支持多線程所以不需要考慮這個(gè)問題了。
2、工廠模式
實(shí)現(xiàn):定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。
應(yīng)用場(chǎng)景:眾多子類并且會(huì)擴(kuò)充、創(chuàng)建方法比較復(fù)雜。
<?php /** * 優(yōu)才網(wǎng)公開課示例代碼 * * 工廠模式 * * @author 優(yōu)才網(wǎng)全棧工程師教研組 * @see http://www.ucai.cn */ //抽象產(chǎn)品 interfacePerson { public function getName(); } //具體產(chǎn)品實(shí)現(xiàn) classTeacher implements Person { function getName() { return "老師\n"; } } classStudent implements Person { function getName() { return "學(xué)生\n"; } } //簡(jiǎn)單工廠 classSimpleFactory { public static function getPerson($type) { $person = null; if ($type == 'teacher') { $person = new Teacher(); } elseif ($type == 'student') { $person = new Student(); } return $person; } } //簡(jiǎn)單工廠調(diào)用 classSimpleClient { function main() { // 如果不用工廠模式,則需要提前指定具體類 // $person = new Teacher(); // echo $person->getName(); // $person = new Student(); // echo $person->getName(); // 用工廠模式,則不需要知道對(duì)象由什么類產(chǎn)生,交給工廠去決定 $person =SimpleFactory::getPerson('teacher'); echo $person->getName(); $person =SimpleFactory::getPerson('student'); echo $person->getName(); } } //工廠方法 interfaceCommFactory { public function getPerson(); } //具體工廠實(shí)現(xiàn) classStudentFactory implements CommFactory { function getPerson(){ return new Student(); } } classTeacherFactory implements CommFactory { function getPerson() { return new Teacher(); } } //工廠方法調(diào)用 classCommClient { static function main() { $factory= new TeacherFactory(); echo$factory->getPerson()->getName(); $factory= new StudentFactory(); echo$factory->getPerson()->getName(); } } //抽象工廠模式另一條產(chǎn)品線 interfaceGrade { function getYear(); } //另一條產(chǎn)品線的具體產(chǎn)品 classGrade1 implements Grade { public function getYear() { return '2003級(jí)'; } } classGrade2 implements Grade { public function getYear() { return '2004級(jí)'; } } //抽象工廠 interfaceAbstractFactory { function getPerson(); function getGrade(); } //具體工廠可以產(chǎn)生每個(gè)產(chǎn)品線的產(chǎn)品 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(); } } //抽象工廠調(diào)用 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); } } //簡(jiǎn)單工廠 //SimpleClient::main(); //工廠方法 //CommClient::main(); //抽象工廠 FactoryClient::main(); ?>
三種工廠的區(qū)別是,抽象工廠由多條產(chǎn)品線,而工廠方法只有一條產(chǎn)品線,是抽象工廠的簡(jiǎn)化。而工廠方法和簡(jiǎn)單工廠相對(duì),大家初看起來(lái)好像工廠方法增加了許多代碼但是實(shí)現(xiàn)的功能和簡(jiǎn)單工廠一樣。但本質(zhì)是,簡(jiǎn)單工廠并未嚴(yán)格遵循設(shè)計(jì)模式的開閉原則,當(dāng)需要增加新產(chǎn)品時(shí)也需要修改工廠代碼。但是工廠方法則嚴(yán)格遵守開閉原則,模式只負(fù)責(zé)抽象工廠接口,具體工廠交給客戶去擴(kuò)展。在分工時(shí),核心工程師負(fù)責(zé)抽象工廠和抽象產(chǎn)品的定義,業(yè)務(wù)工程師負(fù)責(zé)具體工廠和具體產(chǎn)品的實(shí)現(xiàn)。只要抽象層設(shè)計(jì)的好,框架就是非常穩(wěn)定的。
3、創(chuàng)建者模式
在創(chuàng)建者模式中,客戶端不再負(fù)責(zé)對(duì)象的創(chuàng)建與組裝,而是把這個(gè)對(duì)象創(chuàng)建的責(zé)任交給其具體的創(chuàng)建者類,把組裝的責(zé)任交給組裝類,客戶端支付對(duì)對(duì)象的調(diào)用,從而明確了各個(gè)類的職責(zé)。
應(yīng)用場(chǎng)景:創(chuàng)建非常復(fù)雜,分步驟組裝起來(lái)。
<?php /** * 優(yōu)才網(wǎng)公開課示例代碼 * * 創(chuàng)建者模式 * * @author 優(yōu)才網(wǎng)全棧工程師教研組 * @see http://www.ucai.cn */ //購(gòu)物車 classShoppingCart { //選中的商品 private $_goods = array(); //使用的優(yōu)惠券 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)); } } //假如我們要還原購(gòu)物車的東西,比如用戶關(guān)閉瀏覽器后再打開時(shí)會(huì)根據(jù)cookie還原 $data =array( 'goods' => array('衣服', '鞋子'), 'tickets' => array('減10'), ); //如果不使用創(chuàng)建者模式,則需要業(yè)務(wù)類里一步步還原購(gòu)物車 // $cart= new ShoppingCart(); //foreach ($data['goods'] as $goods) { // $cart->addGoods($goods); // } //foreach ($data['tickets'] as $ticket) { // $cart->addTicket($ticket); // } //$cart->printInfo(); // exit; //我們提供創(chuàng)建者類來(lái)封裝購(gòu)物車的數(shù)據(jù)組裝 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(); ?>
可以看出,使用創(chuàng)建者模式對(duì)內(nèi)部數(shù)據(jù)復(fù)雜的對(duì)象封裝數(shù)據(jù)組裝過程后,對(duì)外接口就會(huì)非常簡(jiǎn)單和規(guī)范,增加修改新數(shù)據(jù)項(xiàng)也不會(huì)對(duì)外部造成任何影響。
4、原型模式
用原型實(shí)例指定創(chuàng)建對(duì)象的種類,并且通過拷貝這個(gè)原型來(lái)創(chuàng)建新的對(duì)象。
應(yīng)用場(chǎng)景: 類的資源非常多、性能和安全要求,一般和工廠方法結(jié)合使用。
<?php /** * 優(yōu)才網(wǎng)公開課示例代碼 * * 原型模式 * * @author 優(yōu)才網(wǎng)全棧工程師教研組 * @see http://www.ucai.cn */ //聲明一個(gè)克隆自身的接口 interfacePrototype { function copy(); } //產(chǎn)品要實(shí)現(xiàn)克隆自身的操作 classStudent implements Prototype { //簡(jiǎn)單起見,這里沒有使用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('清華大學(xué)', '計(jì)算機(jī)', '張三'); $stu1->printInfo(); $stu2 =$stu1->copy(); $stu2->name= '李四'; $stu2->printInfo(); ?>
這里可以看到,如果類的成員變量非常多,如果由外部創(chuàng)建多個(gè)新對(duì)象再一個(gè)個(gè)賦值,則效率不高代碼冗余也容易出錯(cuò),通過原型拷貝復(fù)制自身再進(jìn)行微小修改就是另一個(gè)新對(duì)象了。
設(shè)計(jì)模式的第一部分,創(chuàng)建型模式就總結(jié)完了。下面還有兩部分結(jié)構(gòu)型設(shè)計(jì)模式和行為型設(shè)計(jì)模式稍后繼續(xù)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。