原文詳見:http://www.ucai.cn/blogdetail/7023?mid=1&f=5php
能夠在線運行查看效果哦! 算法
在上一篇咱們講告終構型模式,結構型模式是討論類和對象的結構的。總共有7種。而今天咱們來介紹一下行爲型模式。數據庫
1、什麼是行爲型模式?json
行爲型模式:session
就是描述類和對象之間的通訊和職責的。簡而言之,就是類和對象扮演什麼角色,還有怎麼扮演這個角色的問題。函數
2、行爲型模式的種類post
大致上分爲三個大類:常見模式、已知模式、深度模式測試
常見模式包括: 模版方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、狀態式、職責鏈模式、策略模式this
已知模式包括:備忘錄模式spa
深度模式包括:解釋器模式 訪問者模式
下面來介紹常見模式
一、模版方法模式(Template):
定義一個操做中的算法骨架,而將一些實現步驟延遲到子類當中 實現。就像一個豆漿機,無論放進去的是紅豆仍是黑豆,出來的都是豆漿。
好處:擴展性好,封裝不變的代碼,擴展可變的代碼。
弊端:靈活性差,不能改變骨架部分。
應用場景:一類或一組具備共性的事物中
代碼實現
<?php /** * 優才網公開課示例代碼 * * 模板方法模式 Template * * @author 優才網全棧工程師教研組 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } class Request { public $token = ''; public function __construct() { $this->token = '0c6b7289f5334ed2b697dd461eaf9812'; } } class Response { public function render($content) { output(sprintf('response-render: %s', $content)); } public function redirect($uri) { output(sprintf('response-redirect: %s', $uri)); } public function json($data) { output(sprintf('response-data: %s', json_encode($data))); } } //父類,抽象類 abstract class Controller{ //封裝了輸入輸出 protected $request; protected $response; //返回數據 protected $data = 'data'; public function __construct($request, $response){ $this->request = $request; $this->response = $response; } //執行請求函數,定義整體算法(template method),final防止被複寫(不容許子類改變整體算法) public final function execute(){ $this->before(); if ($this->valid()){ $this->handleRequest(); } $this->after(); } //定義hook method before,作一些具體請求的前置處理 //非abstract方法,子類能夠選擇覆蓋或不覆蓋,默認什麼都不作 protected function before(){ } //定義hook method valid,作請求的數據驗證 //非abstract方法,子類能夠選擇覆蓋或不覆蓋,默認返回驗證經過 protected function valid(){ return true; } //定義hook method handleRequest,處理請求 //定義爲abstract方法,子類必須實現或也聲明爲抽象方法(由子類的子類負責實現) abstract function handleRequest(); //定義hook method after,作一些請求的後置處理 //非abstract方法,子類能夠選擇覆蓋或不覆蓋,默認直接輸出數據 protected function after(){ $this->response->render($this->data); } } //子類1,實現父類開放的具體算法 class User extends Controller{ //覆蓋before方法,實現具體算法,這是一個處理用戶數據操做的控制器 //所以,咱們選擇在before裏面判斷用戶是否已經登陸了,這裏簡單判斷下session數據 function before(){ if (empty($_SESSION['auth'])){ //沒登陸就直接跳轉了,再也不執行後續的操做 $this->response->redirect("user/login.php"); } } //覆蓋valid方法,這裏咱們驗證用戶提交數據中有沒有帶驗證token function valid(){ if (isset($this->request->token)){ return true; } return false; } //覆蓋handleRequest方法,必選,覺得父類中聲明瞭abstract了 function handleRequest(){ //作具體處理,通常根據參數執行不一樣的業務邏輯 } //這個類咱們選擇不覆蓋after方法,使用默認處理方式 } //子類2,實現父類開放的具體算法 class Post extends Controller{ //這個類咱們選擇不覆蓋before方法,使用默認處理方式 //這個類咱們選擇不覆蓋valid方法,使用默認處理方式 //覆蓋handleRequest方法,必選,覺得父類中聲明瞭abstract了 function handleRequest(){ //作具體處理,通常根據參數執行不一樣的業務邏輯 $this->data = array('title' => 'ucai'); } //覆蓋after方法,使用json格式輸出數據 function after(){ $this->response->json($this->data); } } class Client { public static function test(){ $request = new Request(); $response = new Response(); //最終調用 $user = new User($request, $response); $user->execute(); //最終調用 $post = new Post($request, $response); $post->execute(); } } Client::test();
二、命令模式(Command) :
行爲請求者與行爲實現者解耦。就像軍隊裏的「敬禮」,不論是誰聽到這個命令都會作出標準的敬禮動做。
好處:便於添加和修改行爲,便於聚合多個命令。
弊端:形成過多具體的命令類。
應用場景:對要操做的對象,進行的相同操做。
代碼實現
<?php /** * 優才網公開課示例代碼 * * 命令模式 Command * * @author 優才網全棧工程師教研組 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } class Document { private $name = ''; public function __construct($name) { $this->name = $name; } public function showText() { output(sprintf("showText: %s", $this->name)); } public function undo() { output(sprintf("undo-showText: %s", $this->name)); } } class Graphics { private $name = ''; public function __construct($name) { $this->name = $name; } public function drawCircle() { output(sprintf("drawCircle: %s", $this->name)); } public function undo() { output(sprintf("undo-drawCircle: %s", $this->name)); } } class Client { public static function test() { $document = new Document('A'); $graphics = new Graphics('B'); $document->showText(); $graphics->drawCircle(); $document->undo(); } } Client::test(); <?php /** * 優才網公開課示例代碼 * * 命令模式 Command * * @author 優才網全棧工程師教研組 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } interface Command { public function execute(); public function undo(); } class Document implements Command { private $name = ''; public function __construct($name) { $this->name = $name; } public function execute() { output(sprintf("showText: %s", $this->name)); } public function undo() { output(sprintf("undo-showText: %s", $this->name)); } } class Graphics implements Command { private $name = ''; public function __construct($name) { $this->name = $name; } public function execute() { output(sprintf("drawCircle: %s", $this->name)); } public function undo() { output(sprintf("undo-drawCircle: %s", $this->name)); } } class Client { public static function test() { $array = array(); array_push($array, new Document('A')); array_push($array, new Document('B')); array_push($array, new Graphics('C')); array_push($array, new Graphics('D')); foreach ($array as $command) { $command->execute(); } $top = array_pop($array); $top->undo(); } } Client::test(); <?php /** * 優才網公開課示例代碼 * * 命令模式 Command * * @author 優才網全棧工程師教研組 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } interface Command { public function execute(); public function undo(); } class Document { private $name = ''; public function __construct($name) { $this->name = $name; } public function showText() { output(sprintf("showText: %s", $this->name)); } public function undo() { output(sprintf("undo-showText: %s", $this->name)); } } class Graphics { private $name = ''; public function __construct($name) { $this->name = $name; } public function drawCircle() { output(sprintf("drawCircle: %s", $this->name)); } public function undo() { output(sprintf("undo-drawCircle: %s", $this->name)); } } class DocumentCommand implements Command { private $obj = ''; public function __construct(Document $document) { $this->obj = $document; } public function execute() { $this->obj->showText(); } public function undo() { $this->obj->undo(); } } class GraphicsCommand implements Command { private $obj = ''; public function __construct(Graphics $graphics) { $this->obj = $graphics; } public function execute() { $this->obj->drawCircle(); } public function undo() { $this->obj->undo(); } } class Client { public static function test() { $array = array(); array_push($array, new DocumentCommand(new Document('A'))); array_push($array, new DocumentCommand(new Document('B'))); array_push($array, new GraphicsCommand(new Graphics('C'))); array_push($array, new GraphicsCommand(new Graphics('D'))); foreach ($array as $command) { $command->execute(); } $top = array_pop($array); $top->undo(); } } Client::test();
三、迭代器模式 (Iterator):
訪問聚合對象內容而不暴露內部結構。就像一個雙色球彩票開獎同樣,每次都是搖出七個球,不能能搖不是七個球的中獎號碼組合。
好處:以不一樣方式遍歷一個集合。
弊端:每次遍歷都是整個集合,不能單獨取出元素。
應用場景:須要操做集合裏的所有元素。
代碼實現
<?php /** * 優才網公開課示例代碼 * * 迭代器模式 Iterator * * @author 優才網全棧工程師教研組 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } class RecordIterator implements Iterator{ private $position = 0; //注意:被迭代對象屬性是私有的 private $records = array(); public function __construct(Array $records) { $this->position = 0; $this->records = $records; } function rewind() { $this->position = 0; } function current() { return $this->records[$this->position]; } function key() { return $this->position; } function next() { ++$this->position; } function valid() { return isset($this->records[$this->position]); } } class PostListPager { protected $record = array(); protected $total = 0; protected $page = 0; protected $size = 0; public function __construct($category, $page, $size) { $this->page = $page; $this->size = $size; // query db $total = 28; $this->total = $total; $record = array( 0 => array('id' => '1'), 1 => array('id' => '2'), 2 => array('id' => '3'), 3 => array('id' => '4'), ); // $this->record = $record; } public function getIterator() { return new RecordIterator($this->record); } public function getMaxPage() { $max = intval($this->total / $this->size); return $max; } public function getPrevPage() { return max($this->page - 1, 1); } public function getNextPage() { return min($this->page + 1, $this->getMaxPage()); } } class Client { public static function test(){ $pager = new PostListPager(1, 2, 4); foreach ($pager->getIterator() as $key => $val) { output(sprintf('Key[%d],Val[%s]', $key, json_encode($val))); } output(sprintf('MaxPage[%d]', $pager->getMaxPage())); output(sprintf('Prev[%d]', $pager->getPrevPage())); output(sprintf('Next[%d]', $pager->getNextPage())); $iterator = $pager->getIterator(); while($iterator->valid()){ print_r($iterator->current()); $iterator->next(); } $iterator->rewind(); } } Client::test();
四、觀察者模式(Observer) :
又叫發佈訂閱模式,當一個主體對象發生改變時,依賴它的多個觀察者對象 都獲得通知並自動更新響應。就像報社同樣,今天發佈的消息只要是看這份報紙的人看到的都是一樣的內容。若是發佈另外一份報紙,也是同樣的。
好處:廣播式通訊,範圍大,一呼百應,便於操做一個組團,「公有制」。
弊端:不能單獨操做組團裏的個體,不能實行按需分配。
應用場景:操做多個對象,並操做相同。
代碼實現:
<?php /** * 優才網公開課示例代碼 * * 觀察者模式 Observer * * @author 優才網全棧工程師教研組 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } //訂單數據對象簡單模擬,這個是實際須要被觀察的對象(Subject),可是咱們將其獨立,而後 //經過構造方法傳入到咱們模式中的Subject中,這樣使具體業務更加獨立 class Order{ //訂單號 private $id = ''; //用戶ID private $userId = ''; //用戶名 private $userName = ''; //價格 private $price = ''; //下單時間 private $orderTime = ''; //訂單數據填充簡單模擬,實際應用中可能會讀取用戶表單輸入並處理 public function __set($name, $value){ if (isset($this->$name)){ $this->$name = $value; } } //獲取訂單屬性 public function __get($name){ if (isset($this->$name)){ return $this->$name; } return ""; } } //假設的DB類,便於測試,實際會存入真實數據庫 class FakeDB{ public function save($data){ return true; } } class Client { public static function test() { //初始化一個訂單數據 $order = new Order(); $order->id = 1001; $order->userId = 9527; $order->userName = "God"; $order->price = 20.0; $order->orderTime = time(); //向數據庫保存訂單 $db = new FakeDB(); $result = $db->save($order); if ($result){ //實際應用可能會寫到日誌文件中,這裏直接輸出 output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" ); //實際應用會調用郵件發送服務如sendmail,這裏直接輸出 output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" ); //實際應用會調用郵件發送服務如sendmail,這裏直接輸出 output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" ); } } } Client::test();
<?php /** * 優才網公開課示例代碼 * * 觀察者模式 Observer * * @author 優才網全棧工程師教研組 * @see http://www.ucai.cn */ function output($string) { echo $string . "\n"; } //訂單數據對象簡單模擬,這個是實際須要被觀察的對象(Subject),可是咱們將其獨立,而後 //經過構造方法傳入到咱們模式中的Subject中,這樣使具體業務更加獨立 class Order{ //訂單號 private $id = ''; //用戶ID private $userId = ''; //用戶名 private $userName = ''; //價格 private $price = ''; //下單時間 private $orderTime = ''; //訂單數據填充簡單模擬,實際應用中可能會讀取用戶表單輸入並處理 public function __set($name, $value){ if (isset($this->$name)){ $this->$name = $value; } } //獲取訂單屬性 public function __get($name){ if (isset($this->$name)){ return $this->$name; } return ""; } } //被觀察者, 負責維護觀察者並在變化發生是通知觀察者 class OrderSubject implements SplSubject { private $observers; private $order; public function __construct(Order $order) { $this->observers = new SplObjectStorage(); $this->order = $order; } //增長一個觀察者 public function attach(SplObserver $observer) { $this->observers->attach($observer); } //移除一個觀察者 public function detach(SplObserver $observer) { $this->observers->detach($observer); } //通知全部觀察者 public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } //返回主體對象的具體實現,供觀察者調用 public function getOrder() { return $this->order; } } //記錄業務數據日誌 (ActionLogObserver),實際可能還要抽象一層以處理不一樣的Action(業務操做),這裏省略 class ActionLogObserver implements SplObserver{ public function update(SplSubject $subject) { $order = $subject->getOrder(); //實際應用可能會寫到日誌文件中,這裏直接輸出 output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" ); } } //給用戶發送訂單確認郵件 (UserMailObserver) class UserMailObserver implements SplObserver{ public function update(SplSubject $subject) { $order = $subject->getOrder(); //實際應用會調用郵件發送服務如sendmail,這裏直接輸出 output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" ); } } //給管理人員發訂單處理通知郵件 (AdminMailObserver) class AdminMailObserver implements SplObserver{ public function update(SplSubject $subject) { $order = $subject->getOrder(); //實際應用會調用郵件發送服務如sendmail,這裏直接輸出 output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" ); } } //假設的DB類,便於測試,實際會存入真實數據庫 class FakeDB{ public function save($data){ return true; } } class Client { public static function test() { //初始化一個訂單數據 $order = new Order(); $order->id = 1001; $order->userId = 9527; $order->userName = "God"; $order->price = 20.0; $order->orderTime = time(); //綁定觀察者 $subject = new OrderSubject($order); $actionLogObserver = new ActionLogObserver(); $userMailObserver = new UserMailObserver(); $adminMailObserver = new AdminMailObserver(); $subject->attach($actionLogObserver); $subject->attach($userMailObserver); $subject->attach($adminMailObserver); //向數據庫保存訂單 $db = new FakeDB(); $result = $db->save($order); if ($result){ //通知觀察者 $subject->notify(); } } } Client::test();
《接下一篇》