PHP結構型設計模式(上)

結構型設計模式: GOF 23個設計模式中,屬於結構型設計模式7個。分別爲適配器模式裝飾器模式代理模式外觀模式橋接模式組合模式享元模式php


PHP設計模式(五)—適配器模式(Adapter Pattern)

適配器模式(Adapter Pattern):將某個對象的接口適配爲另外一個對象所指望的接口。屬於結構型設計模式。mysql

(一)爲何須要適配器模式sql

1,某個操做數據庫的有兩套不一樣的數據庫操做方法,咱們經過適配器統一成一個接口。例如,咱們待會把mysql和mysqli統一成一個接口。數據庫

2,咱們有多套數據庫對應了多種數據庫操做,例如MySQL,SqlServer,Oralce,Redis都有對應的操做函數,或操做類。PDO把這些都統一成一個接口。設計模式

3,系統的增長一些新功能,建立了一個新的接口,可是老的接口並不想廢棄。可使用適配器模式,對用戶隱藏這兩個接口,提供用戶所但願的接口。安全

(二)適配器UML圖bash

Adapter Pattern

(三)設計實例微信

把MySQL和mysqli統一成一個接口,用戶能夠調用一樣的方法使用MySQL和mysqli操做數據庫。函數

<?php
//MySQL待操做適配類
class MySQLAdaptee implements Target
{
    protected $conn;    //用於存放數據庫鏈接句柄
    //實現鏈接方法
    public function connect($host, $user, $passwd, $dbname)
    {
        $conn = mysql_connect($host, $user, $passwd);
        mysql_select_db($dbname, $conn);
        $this->conn = $conn;
    }
    //查詢方法
    public function query($sql)
    {
        $res = mysql_query($sql, $this->conn);
        return $res;
    }
    //關閉方法
    public function close()
    {
        mysql_close($this->conn);
    }
}
//MySQLi操做待適配類
class MySQLiAdaptee 
{
    protected $conn;
    public function connect($host, $user, $passwd, $dbname)
    {
        $conn = mysqli_connect($host, $user, $passwd, $dbname);
        $this->conn = $conn;
    }
    public function query($sql)
    {
        return mysqli_query($this->conn, $sql);
    }
    public function close()
    {
        mysqli_close($this->conn);
    }
}
//用戶所期待的接口
Interface Target{
    public function connect($host, $user, $passwd, $dbname);
    public function query($sql);
    public function close();
}
//用戶期待適配類
Class DataBase implements Target {
    protected $db ;     //存放MySQLiAdapter對象或MySQLAdapter對象
    public function  __construct($type){
        $type = $type."Adapter" ;
        $this->db = new $type ;
    }
    public function connect($host, $user, $passwd, $dbname){
        $this->db->connect($host, $user, $passwd, $dbname);
    }
    public function query($sql){
        return $this->db->query($sql);
    }
    public function close(){
        $this->db->close();
    }
}
//用戶調用同一個接口,使用MySQL和mysqli這兩套不一樣示例。
$db1 = new DataBase('MySQL');
$db1->connect('127.0.0.1','root','1234','myDB');die;
$db1->query('select * from test');
$db1->close();

$db2 = new DataBase('MySQLi');
$db2->connect('127.0.0.1','root','1234','myDB');
$db2->query('select * from test');
$db2->close();
複製代碼

上面的代碼只是一個示例,若是你運行以上的代碼報了mysql函數不存在或是被廢棄的錯誤。這是正常的,由於MySQL這套函數在PHP5.5以上的版本已經被廢棄了。感興趣的還能夠去了解一下PDO的實現。 經過上面的代碼,咱們能夠看到,使用適配器能夠把不一樣的操做接口封裝起來,對外顯示成用戶所指望的接口。post

這就比如你家牆上有一個電源三相插孔,可是插孔的孔距之間過小。你的電器三相插頭插腳距太大的插不進去,或許你還有個兩相的插頭,或許你還有條USB線和type-C線,這些都無法插到三相接口裏。因而你買了個插腳適合插到你牆上的排插,而後這個排插是這些年新出的,USB也能插。因而你把你的三相插頭,兩相插頭,USB線,type-c線都插到排插上。實際上就是間接地連在了你牆壁上的三相插孔上。

沒錯,適配器要作的就是這麼回事。

有些書也把適配器模式分爲:類的適配器模式,對象的適配器模式,接口的適配器模式


PHP設計模式(六)—裝飾器模式(Decorator Pattern)

裝飾器模式(Decorator Pattern): 容許向一個已有的對象添加新的功能或部份內容,同時又不改變其結構。屬於結構型模式,它是做爲現有的類的一個包裝。

(一)爲何須要裝飾器模式:

1,咱們要對一個已有的對象添加新功能,又不想修改它原來的結構。

2,使用子類繼承的方法去實現添加新功能,會不可避免地出現子類過多,繼承鏈很長的狀況。並且很多書籍都規勸咱們竭力保持一個對象的父與子關係不超過3個。

3,裝飾器模式,能夠提供對對象內容快速非侵入式地修改。

(二)裝飾器模式UML圖

Decorator Pattern

(三)簡單實例

若是有一個遊戲角色,他原來就是默認穿一件長衫。如今遊戲改進了,以爲這個角色,除了穿一件長衫前,還能夠在裏面穿一件袍子,或是一件球衣。在外面穿一套盔甲或是宇航服。

<?php
/*遊戲原來的角色類
class Person{
    public function clothes(){
        echo "長衫".PHP_EOL;
    }
}
*/

//裝飾器接口
interface Decorator
{
   public function beforeDraw();
   public function afterDraw();
}
//具體裝飾器1-宇航員裝飾
class AstronautDecorator implements Decorator
{
    public function beforeDraw()
    {
        echo "穿上T恤".PHP_EOL;
    }
    function afterDraw()
    {
        echo "穿上宇航服".PHP_EOL;
        echo "穿戴完畢".PHP_EOL;
    }
}
//具體裝飾器2-警察裝飾
class PoliceDecorator implements Decorator{
    public function beforeDraw()
    {
        echo "穿上警服".PHP_EOL;
    }
    function afterDraw()
    {
        echo "穿上防彈衣".PHP_EOL;
        echo "穿戴完畢".PHP_EOL;
    }
}
//被裝飾者
class Person{
    protected $decorators = array(); //存放裝飾器
    //添加裝飾器
    public function addDecorator(Decorator $decorator)
    {
        $this->decorators[] = $decorator;
    }
    //全部裝飾器的穿長衫前方法調用
    public function beforeDraw()
    {
        foreach($this->decorators as $decorator)
        {
            $decorator->beforeDraw();
        }
    }
    //全部裝飾器的穿長衫後方法調用
    public function afterDraw()
    {
        $decorators = array_reverse($this->decorators);
        foreach($decorators as $decorator)
        {
            $decorator->afterDraw();
        }
    }
    //裝飾方法
    public function clothes(){
        $this->beforeDraw();
        echo "穿上長衫".PHP_EOL;
        $this->afterDraw();
    }
}
//警察裝飾
$police = new Person;
$police->addDecorator(new PoliceDecorator);
$police->clothes();
//宇航員裝飾
$astronaut = new Person;
$astronaut->addDecorator(new AstronautDecorator);
$astronaut->clothes();
//混搭風
$madman = new Person;
$madman->addDecorator(new PoliceDecorator);
$madman->addDecorator(new AstronautDecorator);
$madman->clothes();
複製代碼

固然,上面的代碼沒有嚴格地按照UML圖,這是由於當被裝飾者只有一個時,那 Component也就是ConcreteComponent。同理,若是,只有一個裝飾器,那也不必實現一個implment接口。

若是咱們有兩個不一樣的被裝飾者,那固然就應該抽象出一個Component,讓這兩個被裝飾者去繼承它。也許,那繼承不是又來了嗎。既然外面都用到繼承,直接把裝飾器的方法放到繼承裏面不就好了。

但是你想,若是,直接經過繼承的話,那裝飾過的被裝飾者就應該繼承自被裝飾者,並且被裝飾者由於裝飾的不一樣還要有不少不一樣類型的子類。而使用裝飾者模式的話,繼承鏈縮短了,並且不一樣的裝飾類型還能夠動態增長。


PHP設計模式(七)—代理模式(Proxy Pattern)

代理模式(Proxy Pattern):構建了透明置於兩個不一樣對象以內的一個對象,從而可以截取或代理這兩個對象間的通訊或訪問。

(一)爲何須要代理模式

1,遠程代理,也就是爲了一個對象在不一樣地址空間提供局部表明。隱藏一個對象存在於不一樣地址空間的事實。

2,虛擬代理,根據須要來建立開銷很大的對象,經過它來存放實例化須要很長時間的真實對象。

3,安全代理,用來控制真實對象的訪問對象。

4,智能指引,當調用真實對象的時候,代理處理一些事情。

(二)代理模式UML圖

Proxy Pattern

(三)簡單實例

案例一:你想買一張學友哥的新唱片,之前你都是在縣城CD店裏買的。如今CD行業不景氣,沒得賣了。你只能找人去香港幫你代購一張。

<?php
//代理抽象接口
interface shop{
    public function buy($title);
}
//原來的CD商店,被代理對象
class CDshop implements shop{
    public function buy($title){
        echo "購買成功,這是你的《{$title}》唱片".PHP_EOL;
    }
}
//CD代理
class Proxy implements shop{
    public function buy($title){
        $this->go();
        $CDshop = new CDshop;
        $CDshop->buy($title);
    }
    public function go(){
        echo "跑去香港代購".PHP_EOL;
    }
}

//你93年買了張 吻別
$CDshop = new CDshop;
$CDshop->buy("吻別");
//14年你想買張 醒着作夢 找不到CD商店了,和作夢似的,不得不找了個代理去香港幫你代購。
$proxy = new Proxy;
$proxy->buy("醒着作夢");
複製代碼

案例二:經過代理實現MySQL的讀寫分離,若是是讀操做,就鏈接127.0.0.1的數據庫,寫操做就讀取127.0.0.2的數據庫

<?php
class Proxy
{   
    protected $reader;
    protected $wirter;
    public function __construct(){
        $this->reader = new PDO('mysql:host=127.0.0.1;port=3306;dbname=CD;','root','password');
        $this->writer = new PDO('mysql:host=127.0.0.2;port=3306;dbname=CD;','root','password');
    }
    public function query($sql)
    {
        if (substr($sql, 0, 6) == 'select')
        {
            echo "讀操做: ".PHP_EOL;
            return $this->reader->query($sql);
        }
        else
        {
            echo "寫操做:".PHP_EOL;
            return  $this->writer->query($sql);
        }
    }
}
//數據庫代理
$proxy = new Proxy;
//讀操做
$proxy->query("select * from table");
//寫操做
$proxy->query("INSERT INTO table SET title = 'hello' where id = 1");


//固然對於數據庫來講,這裏應該使用單例模式的方法來存放$reader$writer,但我只是舉個例子,不想把單例加進來把代碼搞複雜。
//可是若是你要實現這樣的一個數據庫代理,我以爲仍是有必要用上單例模式的知識
複製代碼

一句話來講,代理模式,就是在訪問對象時經過一個代理對象去訪問你想訪問的對象。而在代理對象中,咱們能夠實現對訪問對象的截斷或權限控制等操做。


PHP設計模式(八)—外觀模式(Facade Pattern)

外觀模式 (Facade Pattern): 爲子系統中的一組接口提供一個一致的界面,定義一個高層接口,這個接口使得這一子系統更加容易使用。

(一)爲何須要外觀模式

1,開發階段,子系統愈來愈複雜,增長外觀模式提供一個簡單的調用接口。

2,維護一個大型遺留系統的時候,可能這個系統已經很是難以維護和擴展,但又包含很是重要的功能,爲其開發一個外觀類,以便新系統與其交互。

3,外觀模式能夠隱藏來自調用對象的複雜性。

(二)外觀模式UML圖

Facade Pattern

(三)簡單實例

好比說咱們去醫院就診,醫院有醫生員工系統,有藥品系統,有患者資料系統。可是咱們只是在前臺掛個號,就能在其餘系統裏都看到咱們。外觀系統就差很少這樣。

若是沒有掛號系統的話,咱們就先要去醫生系統通知一下醫生, 而後去患者系統取一下患者資料交給醫生,再去藥品系統登記一下,最後到藥房領藥。

<?php
//醫院醫生員工系統
class DoctorSystem{

    //通知就診醫生
    static public function getDoctor($name){
        echo __CLASS__.":".$name."醫生,掛你號".PHP_EOL;
        return new Doctor($name);
    }
}
//醫生類
class Doctor{
    public $name;
    public function __construct($name){
        $this->name = $name;
    }
    public function prescribe($data){
        echo __CLASS__.":"."開個處方給你".PHP_EOL;
        return "祖傳祕方,藥到必死";
    }
}
//患者系統
class SufferSystem {
    static function getData($suffer){
        $data = $suffer."資料";
        echo  __CLASS__.":".$suffer."的資料是這些".PHP_EOL ;
        return  $data;
    }
}
//醫藥系統
class MedicineSystem {
    static function register($prescribe){
        echo __CLASS__.":"."拿處處方:".$prescribe."------------通知藥房發藥了".PHP_EOL;
        Shop::setMedicine("砒霜5千克");
    }
}
//藥房
class shop{
    static public $medicine;
    static function setMedicine($medicine){
        self::$medicine = $medicine;
    }
    static function getMedicine(){
        echo __CLASS__.":".self::$medicine.PHP_EOL;
    }
}

//若是沒有掛號系統,咱們就診的第一步
//通知就診醫生
$doct = DoctorSystem::getDoctor("顧夕衣");
//患者系統拿病歷資料
$data = SufferSystem::getData("何在");
//醫生看病歷資料,開處方
$prscirbe = $doct->prescribe($data);
//醫藥系統登記處方
MedicineSystem::register($prscirbe);
//藥房拿藥
Shop::getMedicine();

echo PHP_EOL.PHP_EOL."--------有了掛號系統之後--------".PHP_EOL.PHP_EOL;

//掛號系統
class Facade{
    static public function regist($suffer,$doct){
        $doct = DoctorSystem::getDoctor($doct);
        //患者系統拿病歷資料
        $data = SufferSystem::getData($suffer);
        //醫生看病歷資料,開處方
        $prscirbe = $doct->prescribe($data);
        //醫藥系統登記處方
        MedicineSystem::register($prscirbe);
        //藥房拿藥
        Shop::getMedicine();
    }
}
//患者只須要掛一個號,其餘的就讓掛號系統去作吧。
Facade::regist("葉好龍","賈中一");
複製代碼

外觀模式,也叫門面模式。它多用於在多個子系統之間,做爲中間層。用戶經過Facade對象,直接請求工做,省去了用戶調用多個子系統的複雜動做。

外觀模式常舉的一個例子,就是咱們買了好多支股票,可是時間有限。盯盤很複雜,咱們搞得一團糟。因此,咱們乾脆買了股票基金。股票基金就比如於外觀模式的Facade對象,而子系統就是股票基金投的各支股票。


上一篇PHP建立型設計模式

感謝閱讀,因爲筆者也是初學設計模式,能力有限,文章不可避免地有失偏頗 後續更新** PHP設計模式-結構型設計模式(下) **介紹,歡迎你們評論指正


我最近的學習總結:


歡迎你們關注個人微信公衆號 火風鼎
相關文章
相關標籤/搜索