設計模式應用的目的是封裝代碼中可變的部分,提升代碼的複用性,實現代碼設計的高內聚,低耦合的原則。把應用程序中常常要變更的和那些不怎麼變更的分離出來,把易變的拿出來封裝,使得他不會影響其餘代碼,那麼對其餘代碼的影響就會降到最低,而代碼的可擴展性就更高。php
設計模式中心思想:SOLID原則。(設計模式只是實現這幾種原則在不一樣場景下的應用)算法
1. 方法和對象中儘可能少直接引用其餘對象和方法,而是經過參數注入的方法來傳遞調用。sql
2. 寫代碼時候,每寫一個類,就要考慮這個類的功能是否有擴展的可能,若是擴展我該怎麼作,是否違背開閉原則。數據庫
3. 一般在給類添加功能,莫過於直接修改,或者派生對應的子類擴展,或者是使用組合,顯然前兩種弊端不少,要儘可能使用對象組合的方式。(優先選擇對象組合而不是繼承,這樣一來,子類就不會由於繼承大量沒用的方法和屬性而膨脹。)設計模式
如下經常使用的10中模式解析以及分類:數組
建立模式:工廠模式,單例模式。dom
結構模式:適配器模式,裝飾着模式。this
行爲模式:觀察者模式,策略模式,命令鏈模式,迭代器模式,狀態模式,委託模式。spa
模式的代碼實現結構可能很類似,區別就在於應用在不一樣的場景。設計
開閉原則裏的對擴展開放,對修改關閉,修改指的是修改舊的方法或者類來實現新的功能,而不是說不動代碼。
1. 工廠模式。
您可使用工廠類建立對象,而不直接使用new。這樣,若是您想要更改所建立的對象類型,只需更改該工廠便可。使用該工廠的全部代碼會自動更改。
interface IDB{ function connect($config); } class Mysql implements IDB{ public function connect($config){ return $config; } } class DB{ public function create(){ return new Mysql(); } } $db = DB :: create(); $db -> connect($config);
總結:若是直接new Mysql(),假若有天要換sqlServer數據庫,那就要每一個調用地方都修改了。
2. 單例模式
某些應用程序資源是獨佔的,由於有且只有一個此類型的資源。例如,經過數據庫句柄到數據庫的鏈接是獨佔的。您但願在應用程序中共享數據庫句柄,由於在保持鏈接打開或關閉時,它是一種開銷。
class DB{ static private $_instance = null; static public function getInstance(){ if(empty(self :: $_instance)){ self :: $_instance = new Mysql(); } return self :: $_instance; } }
3. 觀察者模式
觀察者模式爲您提供了避免組件之間緊密耦合的另外一種方法。該模式很是簡單:一個對象經過添加一個方法(該方法容許另外一個對象,即觀察者註冊本身)使自己變得可觀察。當可觀察的對象更改時,它會將消息發送到已註冊的觀察者。這些觀察者使用該信息執行的操做與可觀察的對象無關。結果是對象能夠相互對話,而沒必要了解緣由。
interface IObserver{ public function onChange( $sender, $args ); } interface IObserable{ public function addObserver( $observer ); } class UserListLog implements IObserver{ //觀察者 public function onChange( $sender, $args ){ return "observer: $args"; } } class UserList implements IObserable{ private $_observers = []; public function addObserver($observer){ $this -> _observers[] = $observer; } public function addCustomer( $name ){ foreach($this -> _observers as $observer){ //觸發對全部註冊的觀察者方法的調用 $observer -> onChange( $this, $name ); } } } $ul = new UserList();
總結:添加了客戶,就調用全部註冊的操做對象的方法,使得用戶操做和觀察者徹底分離,若是再添加其餘的觀察者就不用修改內部代碼,只用註冊新的觀察者就好。固然你也能夠加刪除觀察者的方法。
4. 命令鏈模式
經過一組處理程序發送任意內容。每一個處理程序都會自行判斷本身可否處理請求。若是能夠,該請求被處理,進程中止。您能夠爲系統添加或移除處理程序,而不影響其餘處理程序。
interface ICommand{ function onCommand( $name, $args ); } class MailCommand implements ICommand{ public function onCommand( $name, $args ){ if( $name != 'mail' ) return false; //執行命令的邏輯封裝在每一個命令類裏面,若是不是調用這個命令,就執行下一個命令 echo "Mail : $args"; return true; //若是是就退出命令執行鏈 } } class UserCommand implements ICommand{ public function onCommand( $name, $args ){ if( $name != 'user' ) return false; echo "User: $args"; return true; } } class CommandChain{ private $_commands = []; public function addCommand( $cmd ){ $this -> _commands[] = $cmd; } public function runComand( $name, $args ){ foreach($this -> _commands as $cmd){ if($cmd -> onCommand( $name, $args )){ return; //若是成功執行了命令,就退出命令鏈,若是沒有就繼續往下 } } } } $cc = new CommandChain(); $cc -> addCommand( new UserCommand() ); $cc -> addCommand( new MailCommand() ); $cc -> runCommand( 'user', null); $cc -> runCommand( 'mail', null);
總結:初看,和觀察者結構很像,都是添加,執行。但區別就是場景,命令鏈模式重點在「鏈「,控制執行鏈裏的哪一個命令。
5. 策略模式
在此模式中,算法是從複雜類提取的,於是能夠方便地替換。
interface IStrategy{ public function filter( $record ); } class FindStrategy implements IStrategy{ private $_name; public function __construct( $name ){ $this -> _name = $name; } public function filter( $record ){ return strcmp( $this -> _name, $record ) <= 0; } } class RandomStrategy implements IStrategy{ public function filter( $record ){ return rand(0, 1) >= 0.5; } } class UserList{ private $_list = []; public function __construct( $names ){ if( !empty($names) ){ foreach($names as $name){ $this -> _list[] = $name; } } } public function addUser( $name ){ array_push($this -> _list, $name); } public function find( $filter ){ $recs = []; foreach($this -> _list as $name){ if( $filter -> filter( $name ) ){ $recs[] = $name; } } return $recs; } } $ul = new UserList( array('Jack', 'Tim', 'BoB') ); $f1 = $ul -> find( new FindAfterStrategy('J') ); print_r($f1); $f2 = $ul -> find( new RandomStrategy() ); print_r($f2);
總結:對同一份數據進行不一樣策略處理。策略和數據處理徹底分離,方便擴展和替換。
6. 適配器模式
在須要將一類對象轉換成另外一類對象時,請使用適配器模式。適配器模式適用於代碼重構或者添加新功能,是一種亡羊補牢的模式。將一個類的接口轉換成客戶但願的另一個接口,Adapter模式使得原來因爲接口不兼容而不能一塊兒工做的那此類能夠一塊兒工做
//新需求,適配後須要達到的目標角色 interface ITarget{ public function simpleMethod1(); public function simpleMethod2(); } //舊的須要適配的角色 class Adaptee{ public function simpleMethod1(){ echo "Adaptee simpleMethod1"; } } class Adapter implements ITarget{ private $_adaptee; public function __construct(Adaptee $Adaptee){ $this -> _adaptee = $Adaptee; } public function simpleMethod1(){ $this -> _adaptee -> simpleMethod1(); }; public function simpleMethod2(){ //新添加的功能 echo "Adapter simpleMethod2"; }; } class Client{ public static function main(){ $adaptee = new Adaptee(); $adapter = new Adapter($adaptee); $adapter -> simpleMethod1(); $adapter -> simpleMethod2(); } } Client ::main();
總結: ITarget是新的需求接口,要實現這樣的接口功能,還要沿用原本的Adaptee的方法,構造出Adapter類,在能成功實現調用舊的Adaptee類的simpleMethod1方法的基礎上,實現simpleMethod2的功能。
7. 迭代器模式
迭代器模式將提供一種經過對象集合或對象數組封裝迭代的方法。若是須要遍歷集合中不一樣類型的對象,則使用這種模式尤其便利。
interface IAddressIterator{ //實現循環的退出 public function hasNext(); //實現循環指針的移動 public function next(); } class PersonAddressIterator implements IAddressIterator{ private $_emailAddrs; //指針的初始位置 private $position = 0; public function __construct( $addrs ){ $this -> _emailAddrs = $addrs; } public function hasNext(){ if($this -> position >= sizeof($this -> _emailAddrs) || !isset($this -> _emailAddrs[$this -> position])){ return false; } return true; } public function next(){ $this -> position ++; } public function current(){ return $this -> _emailAddrs[$this -> position]; } } $addr = new PersonAddressIterator(['11@qq.com', '22@qq.com', '33@qq.com', '44@qq.com',]); while($addr -> hasNext()){ echo $addr ->current(); $addr ->next(); }
總結:和php的spl封裝的迭代器類似。
8. 裝飾者模式
經過組合而非繼承的方式,實現了動態擴展對象的功能的能力。有效避免了使用繼承的方式擴展對象功能而帶來的靈活性差,子類無限制擴張的問題。
abstract class Beverage{ //飲料 private $_name; abstract public function cost(); } class Coffee extends Beverage{ public function __construct(){ $this -> _name = 'Coffee'; } public function Cost(){ return 1.00; } } class CondimentDecorator extends Beverage{ //調料類 public function __construct(){ $this -> _name = "Condiment"; } public function Cost(){ return 0.1; } } class Milk extends CondimentDecorator{ private $_beverage; public function __construct($beverage){ $this -> _name = "Milk"; if($beverage instanceof Beverage){ $this -> _beverage = $beverage; } else{ exit('Failure'); } } public function Cost(){ return $this -> _beverage -> Cost() + 0.2; } } class Sugar extends CondimentDecorator{ private $_beverage; public function __construct($beverage){ $this -> _name = "Sugar"; if($beverage instanceof Beverage){ $this -> _beverage = $beverage; } else{ exit('Failure'); } } public function Cost(){ return $this -> _beverage -> Cost() + 0.3; } } $coffee = new Coffee(); $coffee = new Milk($coffee); $coffee = new Sugar($coffee); $coffee -> Cost();
總結:裝飾者(Milk)和被裝飾者(Coffee)必須是同樣的類型。目的是裝飾者必須取代被裝飾者。添加行爲:當裝飾者和組件組合時,就是在加入新的行爲。
9. 委託模式
經過分配或委託至其餘對象,委託設計模式可以去除核心對象中的判決和複雜的功能性。
class Bank{ protected $info; /* 設置基本信息 */ public function updateBankInfo($type, $money){ $this -> info[$type] = $money; } /* 相關操做(包括存款、取款操做) @param int $branktype 操做類型 */ public function brankWithdraw($branktype){ $obj = new $branktype; return $obj -> brankMain($this->info); } } interface Delegate{ //操做方法,實現該接口必須實現的方法 public function brankMain($info); } // 存款操做 class brankDeposit implements Delegate{ public function brankMain($info){ echo $info['deposit']; } } //取款 class brankWithdraw implements Delegate{ public function brandMain($info){ echo $info['withdraw']; } } $bank = new Bank(); $bank->updateBrankInfo("deposit","4000"); $bank->updateBrankInfo("withdraw","2000"); $bank->brankWithdraw("brankDeposit"); echo "<br>"; $bank->brankWithdraw("brankWithdraw");
總結:在傳統方式下,咱們須要判斷當前操做是取款操做仍是存款操做,在分別調用Bank類中的取款操做和存款操做。在委託模式下,咱們將不須要客戶端的判斷操做,對客戶端來講,須要什麼操做,直接傳入操做類型便可,Bank類可自動判斷操做類型,返回相應操做的操做結果。當咱們的操做類型很是多的時候,在客戶端用if else判斷無疑是很可怕的,再假如咱們在不少地方都要有這塊判斷代碼,咱們須要對這些地方的判斷代碼都進行修改(加入後來添加的判斷),而採用委託模式,咱們僅僅須要在新添加的地方添加相應須要的類型便可,不須要改動其它地方的客戶端代碼(很大程度上提升了代碼的複用性)。
對於銀行來講,取款,存款等操做是可變的行爲(假如我再加個開戶),因此單獨分離出來。而委託模式就實現了這一要求,把原本屬於bank的可變操做委託給一個外部的新類,經過調用可變的新類來實現不一樣的操做。
10. 狀態模式
其意圖是容許一個對象在其內部狀態改變時改變它的行爲,對象看起來彷佛修改了他的類。他根據自身的狀態做出不一樣的反應。
interface State{ public function handle($state); public function display(); } class Content{ private $_state = null; public function __construct($state){ $this -> _state = $state; } public function setState($state){ $this -> _state = $state; } public function request(){ $this -> _state -> display(); //經過狀態類來調用自身的類的setSate方法 $this -> _state -> handle($this); } } class StateA implements State{ public function handle($content){ $content -> setState(new StateB()); } public function display(){ echo "state A <br>"; } } class StateB implements State{ public function handle($content){ $content -> setState(new StateC()); } public function display(){ echo "state B <br>"; } } class StateC implements State{ public function handle($content){ $content -> setState(new StateA()); } public function display(){ echo "state C <br>"; } } $obj = new Content(new StateB()); $obj -> request(); $obj -> request(); $obj -> request(); $obj -> request(); $obj -> request();
總結:根據content的不一樣狀態,調用不一樣狀態類下的不一樣操做方法(display,handle是用來切換狀態的)。(一樣的,把可變的狀態操做類提取出來封裝)