溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

php設(shè)計(jì)模式(一):簡(jiǎn)介及創(chuàng)建型模式

發(fā)布時(shí)間:2020-07-08 07:33:54 來(lái)源:網(wǎng)絡(luò) 閱讀:397 作者:ucaicn 欄目:web開發(fā)


我們分三篇文章來(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ù)。


向AI問一下細(xì)節(jié)

免責(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)容。

AI