上一篇:抽象类和接口有什么区别(2020-03-26 11:09:44)
文章大纲

细致入微地讲解php设计模式:观察者模式

2020-03-27 17:20:14

先通过几个生活场景来体会观察者设计模式的好处:

1. 智行App抢火车票,只要你加入了抢票队伍,就不用一直盯着,下单了会直接发信息通知你。

2. 去饭店吃饭,你不用守在厨师旁边,告诉厨师你的号码,以及要点什么菜,然后坐在饭店里刷自己的手机就好,饭菜好了厨师会直接叫你的号码。

3. 某个女孩某次面见你,装扮得极其漂亮,让你一眼就爱上了。然后随后她的一举一动,就可能有时让你欣喜若狂,有时又让你黯然神伤。


如果去用程序代码来实现上面场景的特点,该怎么写呢?


这里我们就拿第二个示例来实践吧。


为了简便,我们这里将餐馆、厨师、服务员统一抽象成一个餐馆类,简单理解成食物提供方,所有顾客都抽象成顾客类,实现程序如下:

<?php

class Restaurant{

    private $guestArr;

    /**
     * 顾客刚下单,等待饭菜
     * @param Guest $obj
     */
    public function addGuest(Guest $obj){
        $this->guestArr[] = $obj;
    }

    /**
     * 顾客的饭菜已经送到了
     * @param Guest $obj
     */
    public function removeGuest(Guest $obj){
        foreach($this->guestArr as $key => $item){
            if( get_class($obj) == get_class($item) ){
                unset($this->guestArr[$key]);
            }
        }
    }

    /**
     * 顾客饭菜刚炒好,通知顾客取餐
     */
    public function notify(){
        foreach($this->guestArr as $item){
            $item->fetchFood();
        }
    }
}

interface Guest{
    public function fetchFood();
}

class A implements Guest{
    public function fetchFood()
    {
        echo get_class($this)." start to fetch food<br>";
    }
}

class B implements Guest{
    public function fetchFood()
    {
        echo get_class($this)." start to fetch food<br>";
    }
}

$restaurant = new Restaurant();
//A客人来点餐了
$restaurant->addGuest(new A);
//A客人饭菜好了,通知取餐
$restaurant->notify();


上面程序有观察者设计模式的中心思想,但例子却不够贴切于观察者模式。


为什么这么说呢?


观察者设计模式,是说A、B、C、D等观察者都有关注某个主题,然后主题有变化的时候,观察此主题的观察者A、B、C、D等就会都收到通知。

上面顾客点餐的例子,实际生活中餐馆里厨师通知取餐,都是一个个通知的,也就是说这个例子里,观察者始终只有一个,通知取餐,一时间也只有一个顾客去取餐。


如果说多人同时点了番茄炒鸡蛋,然后厨师都放一起炒了,然后分开装盘,通知“番茄炒鸡蛋”已好,然后那些点了番茄炒鸡蛋的人,就会同时来取餐。这种情景,就比上面的程序例子,更加贴切于观察者设计模式了:观察者有多个。


好了,现在我们根据上面程序代码,先抽取出观察者设计模式的主要内容,然后再来个更加实际的开发例子来实践。


Restaurant类,就相当于观察者设计模式中的主题,顾客就相当于其中的观察者,区分谁是主题谁是观察者,最简单的方法,就是看哪一方要发起通知。


找出主题的诀窍:发起通知的一方,就是主题。

然后把上面程序里Restaurant改成Subject,Guest替换成Observer,fetchFood方法改成update,基本也就是你网上看到的大多数观察者设计模式的程序示例了(为了简单,本文抛开了抽象类,直接用的具体类)。


最后我们再来一个实际开发经常遇到的例子来实践,加强对观察者模式的理解。


开发示例:用户注册成功,给用户发邮件,给用户手机发短信,给用户发送站内信,给用户送初始福利。


以前可能这样开发:

if( $resiter_result ){
    $emailService = new EmailService();
    $emailService->sendEmail();
    
    $mobileService = new mobileService();
    $mobileservice->sendMessage();

    $modelNote = Model('InsiteNote');
    $modelNote->addNote();
    ...
}


后面随着功能需求的拓展和变动,例如注册成功后,还要给用户送积分,给用户自动关注某些其它用户等等,最终就导致上面那一块的代码,冗长而又臃肿不堪,维护难度逐渐升级。


翟码农用观察者设计模式后,主体代码就如下:

class Client{
    public function registerSucceed(){
        $subject = new Subject();
        $subject->addObserver(new Email());
        $subject->addObserver(new Mobile());
        $subject->addObserver(new Note());
        $subject->notify();
    }
}

$client = new Client();
$client->registerSucceed();

(全部代码可在文末git地址处下载)


发送邮件、发送短信、发送站内信等操作就在各自的观察者类之内,从而达到一种“解耦”的效果。


git地址:待补充


本文为翟码农个人博客里有关设计模式的原创文章,转载请注明出处:http://www.zhai14.com/blog/design-pattern-of-observer-analyzed-with-simple-thought.html



上一篇:抽象类和接口有什么区别(2020-03-26 11:09:44)
我要评论
评论列表