Php設計模式:行為型模式(二)

來源:互聯網
上載者:User
<接上一篇>

4、觀察者模式(Observer):

又叫發布訂閱模式,當一個主體對象發生改變時,依賴它的多個觀察者對象都得到通知並自動更新響應。就像報社一樣,今天發布的訊息只要是看這份報紙的人看到的都是同樣的內容。如果發布另一份報紙,也是一樣的。

好處:廣播式通訊,範圍大,一呼百應,便於操作一個組團,“公有制”。

弊端:不能單獨操作組團裡的個體,不能實行按需分配。

應用情境:操作多個對象,並操作相同。

代碼實現:

[php] view plaincopy

<?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();

5、中介者模式(Mediator):

用中介對象封裝一系列的對象互動,中介使各對象不需要顯式地相互引用。類似於郵局,郵寄者和收件者不用自己跑很遠路,通過郵局就可以。

好處:簡化了對象之間的關係,減少子類的產生。

弊端:中介對象可能變得非常複雜,系統難以維護。

應用情境:不需要顯示地建立互動。

代碼實現:

[php] view plaincopy

<?php    /**  * 優才網公開課範例程式碼  *  * 中介者模式 Mediator  *  * @author 優才網全棧工程師教研組  * @see http://www.ucai.cn  */      function output($string) {      echo    $string . "\n";  }          abstract class Mediator { // 中介者角色      abstract public function send($message,$colleague);   }     abstract class Colleague { // 抽象對象      private $_mediator = null;       public function __construct($mediator) {           $this->_mediator = $mediator;       }       public function send($message) {           $this->_mediator->send($message,$this);       }       abstract public function notify($message);   }     class ConcreteMediator extends Mediator { // 具體中介者角色      private $_colleague1 = null;       private $_colleague2 = null;       public function send($message,$colleague) {           if($colleague == $this->_colleague1) {               $this->_colleague1->notify($message);           } else {               $this->_colleague2->notify($message);           }       }      public function set($colleague1,$colleague2) {           $this->_colleague1 = $colleague1;           $this->_colleague2 = $colleague2;       }   }     class Colleague1 extends Colleague { // 具體對象角色      public function notify($message) {          output(sprintf('Colleague-1: %s', $message));      }   }     class Colleague2 extends Colleague { // 具體對象角色      public function notify($message) {           output(sprintf('Colleague-2: %s', $message));      }   }         class Client {                public static function test(){              // client          $objMediator = new ConcreteMediator();           $objC1 = new Colleague1($objMediator);           $objC2 = new Colleague2($objMediator);           $objMediator->set($objC1,$objC2);           $objC1->send("to c2 from c1");           $objC2->send("to c1 from c2");         }            }        Client::test();

6、狀態模式(State) :

對象在不同狀態下表現出不同的行為。就像女朋友一樣,高興了牽你的手,不高興了遛狗。在兩種狀態下變現出不同的行為。

好處:避免if語句實用,方便增加新狀態,封裝了狀態轉換規則。

弊端:增加系統類別和對象的數量。

應用情境:用於對象的不同功能的轉換。

代碼實現:

[php] view plaincopy

<?php    /**  * 優才網公開課範例程式碼  *  * 狀態模式 State  *  * @author 優才網全棧工程師教研組  * @see http://www.ucai.cn  */    function output($string) {      echo    $string . "\n";  }    abstract class ILift {          //電梯的四個狀態        const OPENING_STATE = 1;  //門敞狀態        const CLOSING_STATE = 2;  //門閉狀態        const RUNNING_STATE = 3;  //運行狀態        const STOPPING_STATE = 4; //停止狀態;                //設定電梯的狀態        public abstract function setState($state);            //首先電梯門開啟動作        public abstract function open();            //電梯門有開啟,那當然也就有關閉了        public abstract function close();            //電梯要能上能下,跑起來        public abstract function run();            //電梯還要能停下來      public abstract function stop();      }        /**   * 電梯的實作類別    */     class Lift extends ILift {          private $state;            public function setState($state) {            $this->state = $state;        }          //電梯門關閉        public function close() {              //電梯在什麼狀態下才能關閉            switch ($this->state) {                case ILift::OPENING_STATE:  //如果是則可以關門,同時修改電梯狀態                    $this->setState(ILift::CLOSING_STATE);                break;                case ILift::CLOSING_STATE:  //如果電梯就是關門狀態,則什麼都不做                    //do nothing;                    return ;                break;                case ILift::RUNNING_STATE: //如果是正在運行,門本來就是關閉的,也說明都不做                    //do nothing;                    return ;                break;                case ILift::STOPPING_STATE:  //如果是停止狀態,本也是關閉的,什麼也不做                    //do nothing;                    return ;                break;            }              output('Lift colse');          }            //電梯門開啟        public function open() {            //電梯在什麼狀態才能開啟            switch($this->state){                case ILift::OPENING_STATE: //如果已經在門敞狀態,則什麼都不做                    //do nothing;                    return ;                break;                case ILift::CLOSING_STATE: //如是電梯時關閉狀態,則可以開啟                    $this->setState(ILift::OPENING_STATE);                break;                case ILift::RUNNING_STATE: //執行狀態,則不能開門,什麼都不做                //do nothing;                    return ;                break;                case ILift::STOPPING_STATE: //停止狀態,淡然要開門了                    $this->setState(ILift::OPENING_STATE);                break;            }            output('Lift open');        }        ///電梯開始跑起來        public function run() {            switch($this->state){                case ILift::OPENING_STATE: //如果已經在門敞狀態,則不你能運行,什麼都不做                    //do nothing;                    return ;                break;                case ILift::CLOSING_STATE: //如是電梯時關閉狀態,則可以運行                    $this->setState(ILift::RUNNING_STATE);                break;                case ILift::RUNNING_STATE: //執行狀態,則什麼都不做                    //do nothing;                    return ;                break;                case ILift::STOPPING_STATE: //停止狀態,可以運行                    $this->setState(ILift::RUNNING_STATE);            }            output('Lift run');        }            //電梯停止        public function stop() {            switch($this->state){                case ILift::OPENING_STATE: //如果已經在門敞狀態,那肯定要先停下來的,什麼都不做                    //do nothing;                    return ;                break;                case ILift::CLOSING_STATE: //如是電梯時關閉狀態,則當然可以停止了                    $this->setState(ILift::CLOSING_STATE);                break;                case ILift::RUNNING_STATE: //執行狀態,有運行當然那也就有停止了                    $this->setState(ILift::CLOSING_STATE);                break;                case ILift::STOPPING_STATE: //停止狀態,什麼都不做                    //do nothing;                    return ;                break;            }            output('Lift stop');        }            }          class Client {            public static function test() {            $lift = new Lift();                            //電梯的初始條件應該是停止狀態             $lift->setState(ILift::STOPPING_STATE);             //首先是電梯門開啟,人進去             $lift->open();                            //然後電梯門關閉             $lift->close();                            //再然後,電梯跑起來,向上或者向下             $lift->run();                   //最後到達目的地,電梯挺下來             $lift->stop();          }    }    Client::test();

<?php    /**  * 優才網公開課範例程式碼  *  * 狀態模式 State  *  * @author 優才網全棧工程師教研組  * @see http://www.ucai.cn  */    function output($string) {      echo    $string . "\n";  }    /**   *    * 定義一個電梯的介面    */     abstract class LiftState{            //定義一個環境角色,也就是封裝狀態的變換引起的功能變化        protected  $_context;            public function setContext(Context $context){            $this->_context = $context;        }            //首先電梯門開啟動作        public abstract function open();            //電梯門有開啟,那當然也就有關閉了        public abstract function close();            //電梯要能上能下,跑起來        public abstract function run();            //電梯還要能停下來,停不下來那就扯淡了        public abstract function stop();        }            /**   * 環境類:定義客戶感興趣的介面。維護一個ConcreteState子類的執行個體,這個執行個體定義目前狀態。   */     class Context {        //定義出所有的電梯狀態        static  $openningState = null;        static  $closeingState = null;        static  $runningState  = null;        static  $stoppingState = null;            public function __construct() {            self::$openningState = new OpenningState();            self::$closeingState = new ClosingState();            self::$runningState =  new RunningState();            self::$stoppingState = new StoppingState();            }            //定一個當前電梯狀態        private  $_liftState;            public function getLiftState() {            return $this->_liftState;        }            public function setLiftState($liftState) {            $this->_liftState = $liftState;            //把當前的環境通知到各個實作類別中            $this->_liftState->setContext($this);        }                public function open(){            $this->_liftState->open();        }            public function close(){            $this->_liftState->close();        }            public function run(){            $this->_liftState->run();        }            public function stop(){            $this->_liftState->stop();        }    }        /**   * 在電梯門開啟的狀態下能做什麼事情    */     class OpenningState extends LiftState {            /**       * 開啟當然可以關閉了,我就想測試一下電梯門開關功能       *       */        public function close() {            //狀態修改            $this->_context->setLiftState(Context::$closeingState);            //動作委託為CloseState來執行            $this->_context->getLiftState()->close();        }            //開啟電梯門        public function open() {            output('lift open...');      }        //門開著電梯就想跑,這電梯,嚇死你!        public function run() {            //do nothing;        }            //開門還不停止?        public function stop() {            //do nothing;        }        }        /**   * 電梯門關閉以後,電梯可以做哪些事情    */     class ClosingState extends LiftState {            //電梯門關閉,這是關閉狀態要實現的動作        public function close() {            output('lift close...');          }        //電梯門關了再開啟,逗你玩呢,那這個允許呀        public function open() {            $this->_context->setLiftState(Context::$openningState);  //置為門敞狀態            $this->_context->getLiftState()->open();        }            //電梯門關了就跑,這是再正常不過了        public function run() {            $this->_context->setLiftState(Context::$runningState); //設定為運行狀態;            $this->_context->getLiftState()->run();        }            //電梯門關著,我就不按樓層                public function stop() {            $this->_context->setLiftState(Context::$stoppingState);  //設定為停止狀態;            $this->_context->getLiftState()->stop();        }        }        /**   * 電梯在運行狀態下能做哪些動作    */     class RunningState extends LiftState {            //電梯門關閉?這是肯定了        public function close() {            //do nothing        }            //啟動並執行時候開電梯門?你瘋了!電梯不會給你開的        public function open() {            //do nothing        }            //這是在運行狀態下要實現的方法        public function run() {            output('lift run...');      }            //這個事絕對是合理的,光運行不停止還有誰敢做這個電梯?!估計只有上帝了        public function stop() {            $this->_context->setLiftState(Context::$stoppingState); //環境設定為停止狀態;            $this->_context->getLiftState()->stop();        }        }                /**   * 在停止狀態下能做什麼事情    */     class StoppingState extends LiftState {            //停止狀態關門?電梯門本來就是關著的!        public function close() {            //do nothing;        }            //停止狀態,開門,那是要的!        public function open() {            $this->_context->setLiftState(Context::$openningState);            $this->_context->getLiftState()->open();        }        //停止狀態再跑起來,正常的很        public function run() {            $this->_context->setLiftState(Context::$runningState);            $this->_context->getLiftState()->run();        }        //停止狀態是怎麼發生的呢?當然是停止方法執行了        public function stop() {            output('lift stop...');      }        }        /**   * 類比電梯的動作    */     class Client {            public static function test() {            $context = new Context();            $context->setLiftState(new ClosingState());                $context->open();            $context->close();            $context->run();            $context->stop();        }    }      Client::test();
  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.