Php設計模式(三):行爲型模式part1

原文詳見: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();

《接下一篇》

相關文章
相關標籤/搜索