Php设计模式(三):行为型模式part1

原文详见:http://www.ucai.cn/blogdetail/7023?mid=1&f=5

可以在线运行查看效果哦!    

 

       在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的。总共有7种。而今天我们来介绍一下行为型模式。

一、什么是行为型模式?

行为型模式:

       就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。

二、行为型模式的种类

       大体上分为三个大类:常见模式、已知模式、深度模式

       常见模式包括: 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态式、职责链模式、策略模式

       已知模式包括:备忘录模式

       深度模式包括:解释器模式 访问者模式

下面来介绍常见模式

  • 常见模式

1、模版方法模式(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(); 

  

2、命令模式(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();

  

3、迭代器模式 (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(); 

  

4、观察者模式(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();

《接下一篇》

  

你可能感兴趣的:(设计模式)