結構型設計模式: GOF 23個設計模式中,屬於結構型設計模式7個。分別為介面卡模式
,裝飾器模式
,代理模式
,外觀模式
,橋接模式
,組合模式
,享元模式
。
PHP設計模式(五)—介面卡模式(Adapter Pattern)
介面卡模式(Adapter Pattern):將某個物件的介面適配為另一個物件所期望的介面。屬於結構型設計模式。
(一)為什麼需要介面卡模式
1,某個運算元據庫的有兩套不同的資料庫操作方法,我們通過介面卡統一成一個介面。例如,我們待會把mysql和mysqli統一成一個介面。
2,我們有多套資料庫對應了多種資料庫操作,例如MySQL,SqlServer,Oralce,Redis都有對應的操作函式,或操作類。PDO把這些都統一成一個介面。
3,系統的增加一些新功能,建立了一個新的介面,但是老的介面並不想廢棄。可以使用介面卡模式,對使用者隱藏這兩個介面,提供使用者所希望的介面。
(二)介面卡UML圖
(三)設計例項
把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的實現。 通過上面的程式碼,我們可以看到,使用介面卡可以把不同的操作介面封裝起來,對外顯示成使用者所期望的介面。
這就好比你家牆上有一個電源三相插孔,但是插孔的孔距之間太小。你的電器三相插頭插腳距太大的插不進去,或許你還有個兩相的插頭,或許你還有條USB線和type-C線,這些都沒法插到三相介面裡。於是你買了個插腳適合插到你牆上的排插,然後這個排插是這些年新出的,USB也能插。於是你把你的三相插頭,兩相插頭,USB線,type-c線都插到排插上。實際上就是間接地連在了你牆壁上的三相插孔上。
沒錯,介面卡要做的就是這麼回事。
有些書也把介面卡模式分為:類的介面卡模式,物件的介面卡模式,介面的介面卡模式
PHP設計模式(六)—裝飾器模式(Decorator Pattern)
裝飾器模式(Decorator Pattern): 允許向一個已有的物件新增新的功能或部分內容,同時又不改變其結構。屬於結構型模式,它是作為現有的類的一個包裝。
(一)為什麼需要裝飾器模式:
1,我們要對一個已有的物件新增新功能,又不想修改它原來的結構。
2,使用子類繼承的方法去實現新增新功能,會不可避免地出現子類過多,繼承鏈很長的情況。而且不少書籍都規勸我們竭力保持一個物件的父與子關係不超過3個。
3,裝飾器模式,可以提供對物件內容快速非侵入式地修改。
(二)裝飾器模式UML圖
(三)簡單例項
如果有一個遊戲角色,他原來就是預設穿一件長衫。現在遊戲改進了,覺得這個角色,除了穿一件長衫前,還可以在裡面穿一件袍子,或是一件球衣。在外面穿一套盔甲或是宇航服。
<?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圖
(三)簡單例項
案例一
:你想買一張學友哥的新唱片,以前你都是在縣城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圖
(三)簡單例項
比如說我們去醫院就診,醫院有醫生員工系統,有藥品系統,有患者資料系統。但是我們只是在前臺掛個號,就能在其他系統裡都看到我們。外觀系統就差不多這樣。
如果沒有掛號系統的話,我們就先要去醫生系統通知一下醫生, 然後去患者系統取一下患者資料交給醫生,再去藥品系統登記一下,最後到藥房領藥。
<?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設計模式-結構型設計模式(下) **介紹,歡迎大家評論指正
我最近的學習總結: