在軟件開發過程當中,常常出現的經典場景的典型解決方案,稱爲設計模式php
如何學習設計模式html
典型場景 --> 典型問題 --> 典型解決辦法java
用來消除邏輯語句.mysql
多態(ploymorphism)是一個生物學上的概念,指同一特種的多種表現形態.sql
在面向對象中,指某種對象實例的不一樣表現形態.數據庫
<?php abstract class Tiger { public abstract function climb(); } class XTiger extends Tiger { public function climb() { echo 'Drop' , '<br/>'; } } class MTiger extends Tiger { public function climb() { echo 'Up' , '<br/>'; } } class Cat { public function climb() { echo 'Fly'; } } class Client { public static function call(Tiger $animal) { // 參數限定不嚴格,能夠更加靈活, 能夠傳遞一個父類類型,就能夠有不一樣的子類形態 $animal->climb(); } } Client::call(new XTiger()); Client::call(new MTiger()); Client::call(new Cat()); ?>
在23種設計模式中,能夠有些模式能夠天然消除的.json
減小new
的操做.設計模式
熟悉概念:安全
調用端
的概念客戶端(Client)
的概念服務器
例如:在Java中,寫了一堆類,最終打成一個包。調用端能夠把包引入,而後使用類中定義的方法,在開發過程當中可使用查看裏面的方法。
假設:開發中引入的是sdk
,都不讓看。如何調用,必須開放一些接口。存在面向接口開發
接口:共同的規則
面向接口開發
<?php // 共同接口 // 服務端打包 interface DB { function conn(); } // 服務端開發(不知道將會被誰調用) class DbMysql implements DB { public function conn() { echo 'conn mysql <br/>'; } } class DbSqlite implements DB { public function conn() { echo 'conn sqlite <br/>'; } } // ***客戶端**** (看不到DbMysql,DbSqlite的內部實現細節) // 只知道,上述兩個類實現了db接口 $db = new DbMysql(); $db->conn(); $db = new DbSqlite(); $db->conn(); // 不管是開發者,仍是調用者都是爲接口負責
模式的做用:
發生鏈接的雙方,知道的越少越好.
在面向對象設計法則中,重要的開閉原則:對於修改是封閉的,對於擴展是開放的.
<?php // 共同接口 // 服務端打包 interface DB { function conn(); } // 服務端開發(不知道將會被誰調用) class DbMysql implements DB { public function conn() { echo 'conn mysql <br/>'; } } class DbSqlite implements DB { public function conn() { echo 'conn sqlite <br/>'; } } // 簡單工廠 class Factory { public static function createDB($type) { if ($type == 'mysql') { return new DbMysql(); } else if ($type == 'sqlite') { return new DbSqlite(); } else { throw new Exception('Error db type', 1); } } } // 客戶端不知道服務端到底有哪些類名, // 只知道對方開放了一個Factory::createDB方法. // 靜態方法容許傳遞數據庫名稱 $msqyl = Factory::createDB('mysql'); $msqyl->conn(); $sqlit = Factory::createDB('sqlite'); $sqlit->conn(); // 若是增長oracle類型,怎麼辦? // 服務端要修改Factory的內容(在Java,C++中,改完以後還得再編譯) // 在面向對象設計法則中,重要的開閉原則 --- 對於修改是封閉的,對於擴展是開放的. // 需求能夠擴展子類來實現。
進行擴展,避免對原有數據進行修改,只須要新增代碼的子類,就能夠完成。
對於修改是封閉的,對於擴展是開放的.
<?php // 共同接口 // 數據庫的接口 interface DB { function conn(); } // 創造數據庫的接口 interface Factory { function createDB(); } // 服務端開發(不知道將會被誰調用) class DbMysql implements DB { public function conn() { echo 'conn mysql <br/>'; } } class DbSqlite implements DB { public function conn() { echo 'conn sqlite <br/>'; } } class MySqlFactory implements Factory { public function createDB() { return new DbMysql(); } } class SqliteFactory implements Factory { public function createDB() { return new DbSqlite(); } } // ==== 服務器端添加oracle類 // 進行擴展,避免對原有數據進行修改 class orcale implements DB { public function conn() { echo 'conn orcal <br />'; } } class orcaleFactory implements Factory { public function createDB() { return new orcale(); } } // ------客戶端開始調用. $fact = new MysqlFactory(); $db = $fact->createDB(); $db->conn(); $fact = new SqliteFactory(); $db = $fact->createDB(); $db->conn();
常見使用場景:
須要數據庫類的時候
操做cookie類
上傳圖片類
DB.class.php Upload.class.php Cookie.class.php // 這三個類都須要讀取配置文件信息,而配置文件是共用的,所以配置讀取類有一個對象就夠了。 // (如何保證對象只有一個)
PHP對象何時全等
二個對象是一個的時候.
單例模式實現
封閉外部new操做
內部開公共接口,負責new操做,控制單一實例
禁止繼承覆蓋__construcotr
防止克隆
<?php /** 單例模式 */ // 第一步,普通類 // class Single { // } // $s1 = new Single(); // $s2 = new Single(); // var_dump($s1, $s2); // var_dump($s1 == $s2); // --------------------- // 第二步,封鎖new操做 // class Single { // // 在new 的時候會觸發魔術函數,__constructor,能夠在 __constructor 魔術函數中作操做 // protected function __constructor() { // 把__constructor()魔術方法保護起來, 致使的後果,一個對象都無法new。(大門關上了,須要開窗) // } // } // $s1 = new Single(); // --------------------- // 第三步,留接口來new對象 // class Single { // public static function getIns() { // getIns的控制權在class內部,能夠在getIns作手腳 // return new self(); // 返回自身實例 // } // protected function __constructor() { // } // } // $s1 = Single::getIns(); // $s2 = Single::getIns(); // var_dump($s1, $s2); // var_dump($s1 == $s2); // --------------------- // 第四步,getIns要預先判斷實例 // class Single { // protected static $ins = null; // public static function getIns() { // getIns的控制權在class內部,能夠在getIns作手腳 // if (self::$ins === null) { // self::$ins = new self(); // } // return self::$ins; // 返回自身實例 // } // protected function __constructor() { // } // } // $s1 = Single::getIns(); // $s2 = Single::getIns(); // var_dump($s1, $s2); // var_dump($s1 == $s2); // true // 問題 :繼承以後 constructor 被公開, 可使用final // class multi extends Single { // public function __constructor() { // 繼承以後 constructor 被公開 // } // } // --------------------- // 第五步,final,防止繼承時,被修改權限 // class Single { // protected static $ins = null; // public static function getIns() { // getIns的控制權在class內部,能夠在getIns作手腳 // if (self::$ins === null) { // self::$ins = new self(); // } // return self::$ins; // 返回自身實例 // } // final protected function __constructor() { // 方法前加 final,則方法不能被覆蓋,類前加final,則類不能被繼承。 // } // } // class multi extends Single { // public function __constructor() { // 繼承以後 constructor 被公開 // } // } // $s1 = Single::getIns(); // $s2 = clone $s1; // 克隆了,又產生了多個對象. // var_dump($s1, $s2); // var_dump($s1 === $s2); // true // --------------------- // 第六步,禁止clone class Single { protected static $ins = null; public static function getIns() { // getIns的控制權在class內部,能夠在getIns作手腳 if (self::$ins === null) { self::$ins = new self(); } return self::$ins; // 返回自身實例 } final protected function __constructor() { // 方法前加 final,則方法不能被覆蓋,類前加final,則類不能被繼承。 } // 封鎖clone final protected function __clone() { } } $s1 = Single::getIns(); $s2 = clone $s1; // 克隆了,又產生了多個對象. var_dump($s1, $s2); var_dump($s1 === $s2); // true
一個對象變化,引發其它對象的反應。可讓其它幾個對象觀察變化的對象的反應.
一對多的關係.
優勢:解耦。
觀察者模式中的三者: Subject
, Observer
, Client
;
// Subject attach() // 記憶多個的對象 detach() // 告知記憶的對象,變化狀況 notify() // 更新通知 // Observer update() // 更新對象中執行的邏輯 // Client // 調用添加觀察者`attach()`
JavaScript實現觀察者模式:
var select = document.querySelector('select'); var content = document.querySelector('.content'); var ad = document.querySelector('.ad'); // Subject select.observer = {}; // 添加觀察者 select.attach = function(key, obj) { this.observer[key] = obj; } // 刪除觀察者 select.detach = function(key) { delete this.observer[key]; } // 更新通知 select.onchange = select.ontify = function() { for (var key in this.observer) { this.observer[key].update(this); } } // Observer // 觀察者 content.update = function(observer) { // 參數是被觀察者對象 alert('content'); if (observer.value) { // 邏輯代碼 } } ad.update = function(observer) { // 參數是被觀察者對象 alert('ad'); if (observer.value) { // 邏輯代碼 } } // Client // 監聽 select.attach('content', content); // 只須要把獨特的表示加入 對象key中 select.attach('ad', ad);
PHP實現觀察者模式:
單一功能原則:類或者一個方法,完成一個功能便可.
PHP中內置接口:
Splsubject
Splobserver
內置類:
Splobjectstorage
<?php // PHP實現觀察者 // PHP5中提供了觀察者(observer)和被觀察者(subject)接口 /** 被觀察者 */ class User implements SplSubject { public $lognum; public $hobby; protected $observers = null; public function __construct($hobby) { $this->lognum = rand(1, 10); $this->hobby = $hobby; $this->observers = new SplObjectStorage(); } public function login() { // 類或者一個方法,完成一個功能便可. (單一功能原則) // 操做session $this->notify(); } public function attach(SplObserver $observer) { $this->observers->attach($observer); } public function detach(SplObserver $observer) { $this->observers->detach($observer); } public function notify() { $this->observers->rewind(); while($this->observers->valid()) { $observer = $this->observers->current(); $observer->update($this); $this->observers->next(); } } } /** 觀察者 */ class Secrity implements SplObserver { public function update(SplSubject $subject) { // 傳入的 對象是$subject,$subject是幹什麼,隨你的意. if($subject->lognum < 3) { echo '這個第' . $subject->lognum . '次安全登陸<br/>'; } else { echo '這個第' . $subject->lognum . '次登陸,異常<br/>'; } } } class Ad implements SplObserver { public function update(SplSubject $subject) { if ($subject->hobby == 'sport') { echo 'sport,nba <br/>'; } else { echo 'good good study, day day up<br/>'; } } } /** Client */ $user = new User('study'); $user->attach(new Secrity()); $user->attach(new Ad()); $user->login();
也稱之爲責任鏈模式(chain of resionbility)
對象產生的過程當中,放在邏輯判斷中.
面向過程和麪向對象混雜在一塊,沒有充分拆開。
責任鏈模式:
權利越大的人管理越多,越嚴重的問題,越得往上級找,底層的人只能管理雞毛蒜皮的小事。
每一個人都有處理事情的,權利範圍.
責任鏈模式最終都須要都一級可以處理.
先處理最近的一級
每一個對象中有職責功能
,上級
.
每一個對象,儲存着對本身上級的引用,若是本身處理不了,交給上一級。
優勢:使用到那一級纔會new
出那一級。
<?php header('content-type: text/html; charset=utf-8'); // 責任鏈模式 // 權利越大的人管理越多,越嚴重的問題,越得往上級找,底層的人只能管理雞毛蒜皮的小事。 // 每一個人都有處理事情的,權利範圍. class borad { public $power = 1; // 處理範圍的權限 protected $top = 'admin'; // 上級的範圍 public function process($lev) { if ($lev <= $this->power) { echo '刪除'; } else { $top = new $this->top; $top->process($lev); } } } class admin { public $power = 2; protected $top = 'police'; public function process($lev) { if ($lev <= $this->power) { echo '封閉'; } else { $top = new $this->top; $top->process($lev); } } } class police { protected $power; protected $top = null; public function process() { echo '抓!~'; } } $lev = 1; $judge = new borad(); // 距離最近的一級 $judge->process($lev);
和工廠模式類似的一種模式
<?php /** Strategy 策略模式 完成:加減乘除功能 */ interface Math { public function calc ($op1, $op2); } class MathAdd implements Math { public function calc($op1, $op2) { return $op1 + $op2; } } class MathSub implements Math { public function calc($op1, $op2) { return $op1 - $op2; } } class MathMul implements Math { public function calc($op1, $op2) { return $op1 * $op2; } } class MathDiv implements Math { public function calc($op1, $op2) { if ($op2 !=0 ) return; return $op1 / $op2; } } // 通常思路: 根據`op`的值,製造對象,而且調用 $op = 'Add'; // 封裝一個虛擬計算器,中能夠 調用到實際計算器 class CMath { protected $calc = null; public function __construct($type) { $calc = 'Math' . $type; $this->calc = new $calc(); } public function calc($op1, $op2) { return $this->calc->calc($op1, $op2); } } $cmath = new CMath($op); var_dump($cmath->calc(10, 100));
工廠方法和策略模式的區別:
工廠方法是傳遞不一樣參數,直接把零件(子類)進行操做。
策略模式是,真實的子類,虛擬成一個父類, 父類中操做(零件)子類。不須要直接碰子類,聚合成一個父類。
問題:繼承層次愈來愈多、
<?php // 場景:發佈一篇文章 class Article { protected $content; public function __construct($content) { $this->content = $content; } public function decorator() { return $this->content; } } $art = new Article('goods goods study, day day up <br/>'); echo $art->decorator(); // ----------------------------------------------------- // 文章須要, 須要編輯人員專門編輯 class BianArt extends article { // 從新加工對decorator public function summary() { return $this->content . '編輯摘要 <br/>'; } } $art = new BianArt('goods goods study, day day'); echo $art->summary(); // ----------------------------------------------------- // 文章須要, 須要作SEO class SeoArt extends BianArt { public function seo() { $content = $this->summary(); return $content . 'seo <br />'; } } $art = new SeoArt('lz'); echo $art->seo(); // ----------------------------------------------------- // 文章須要,廣告部單獨管理 class Ad extends SeoArt { // 層次愈來愈深,目的是:給文章添加各類內容 } // 繼承層次愈來愈多,裝飾器模式能夠解決,變成倆級繼承
使用裝飾器模式,修改多級繼承
父類負責主要邏輯,子類負責裝飾,修飾。(重寫父類的方法,裝飾以後再次返回)
<?php // 裝飾器模式 class BaseArt { protected $content; // 文章內容 protected $art = null; // 文章對象 public function __construct($content) { $this->content = $content; } public function decorator() { return $this->content; } } // 編輯文章 class BianArt extends BaseArt { public function __construct(BaseArt $art) { $this->art = $art; $this->decorator(); } public function decorator() { return $this->content = $this->art->decorator() . '編輯人員,文章摘要'; } } // SEO人員 class SeoArt extends BaseArt { public function __construct(BaseArt $art) { $this->art = $art; $this->decorator(); } public function decorator() { return $this->content = $this->art->decorator() . 'SEO'; } } $art = new BaseArt('day day up'); // echo $art->decorator(); $art1 = new BianArt($art); // echo $art1->decorator(); $art2 = new SeoArt($art1); echo $art2->decorator();
把不適用的格式或者數據類型,轉換成適用目前場景
<?php // 適配器模式 // 服務器端代碼 class tianqi { public static function show() { $today = array('tep' => 28, 'wind' => 7, 'sun' => 1); return serialize($today); } } // 增長一個代理,適配器 class AdapterTainqi extends tianqi { public static function show() { $today = parent::show(); $today = unserialize($today); $today = json_encode($today); return $today; } } // =====客戶端調用===== $taiqni = unserialize(tianqi::show()); echo '溫度:',$taiqni['tep'], '風力:', $taiqni['wind'] , '<br/>'; // java客戶端,並不認識PHP的串行化後的字符串。使用適配器模式,轉成想通模式。 $tq = AdapterTainqi::show(); $td = json_decode($tq); echo '溫度:',$td->tep, '風力:', $td->wind;
在某種場景下,多個條件有共同做用,增長耦合關係,減小邏輯複雜程度
這個世界上的因素都不是單一的,都是相互耦合的.
<?php // 橋接模式 bridge // 功能:論壇站內給用戶發送消息,能夠是站內短信,emial,手機信息 abstract class info { protected $send = null; public function __construct($send) { $this->send = $send; } abstract public function msg($content); public function send($to, $content) { $content = $this->msg($content); $this->send->send($to, $content); } } class zn { public function send($to, $content) { echo 'zn: ', $to, '內容是:', $content; } } class email { public function send($to, $content) { echo 'email: ', $to, '內容是:', $content; } } class sms { public function send($to, $content) { echo 'sms: ', $to, '內容是:', $content; } } class commonInfo extends info { public function msg($content) { return 'common'. $content; } } class warnInfo extends info { public function msg($content) { return 'warn'. $content; } } class dangerInfo extends info { public function msg($content) { return 'danger'. $content; } } // 站內發普通訊息 $commonInfo = new commonInfo(new zn()); $commonInfo->send('小明', '吃晚飯'); // sms發普通訊息 $commonInfo = new commonInfo(new sms()); $commonInfo->send('小紅', '吃晚飯');