当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
我们在用美团App时经常出现用户登录时会弹出一个框告诉用户获得了一张优惠券,金额和使用规则会根据用户距离上次的登录时间和消费习惯来定。当我们经常使用时获得优惠券的概率就会少,优惠金额也会少。软件设计里把登录做为一个被观察的类(主题),把送优惠券做为一个观察类。当用户登录时通知送优惠券,为用增加一个优惠券。可以多个观察者,比如用户登录后会根据其消费习惯给他推荐相关的店铺的广告类。这里有一个广告类也可以定义成一个观察者。
具体代码:
1、定义一个Subject主题接口,所有被观察都都要实现此接口;
/**
* file name: Subject.php
* user : 良子
* date : 2021-01-07
*/
namespace libs\observer;
interface Subject
{
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify();
}
2、定义一个Observer观察者接口,所有观察都都要实现此接口;
/**
* file name: Observer.php
* user : 良子
* date : 2021-01-07
*/
namespace libs\observer;
interface Observer
{
public function update(Subject $subject);
}
3、定义一个用户类User,即被观察对象。包含用户登录,注册等信息
/**
* file name: User.php
* user : 良子
* date : 2021-01-06
*/
namespace libs\observer;
class User implements Subject
{
protected $_observers = [];
//登录次数
public $loginTimes = 0;
//常购买的品类
public $buyCategeory = [];
public function login ($username, $passwd)
{
echo '验证逻辑……此处省略<br/>';
echo '获取用户信息逻辑……此处省略<br/>';
//假设获取用户近一个月内的登录次数
$this->loginTimes = mt_rand(0, 100);
//假设获取用户买得最多 品类
$categeory = ['奶茶', '咖啡', '可乐','水','啤酒'];
$buyCategeory = array_rand($categeory, 2);
foreach ($buyCategeory as $value) {
array_push($this->buyCategeory, $categeory[$value]);
}
return true;
}
public function attach (Observer $observer)
{
$this->_observers[] = $observer;
}
public function detach (Observer $observer)
{
$key = array_search($observer,$this->_observers, true);
if($key){
unset($this->_observers[$key]);
}
}
public function notify ()
{
foreach ($this->_observers as $value) {
$value->update($this);
}
}
}
4、定义一个用户登录后送优惠券的观察者对象ObLoginCoupon,在ObLoginCoupon里只需要引入Coupon类,如果Coupon类由其他同事编写,则可以不需要变动他人的代码;
/**
* file name: Obloin.php
* user : 良子
* date : 2021-01-07
*/
namespace libs\observer;
class ObLoginCoupon implements Observer
{
protected $couponObj = null;
public function update (Subject $subject)
{
$this->couponObj = new Coupon();
//根据用户登录次数送相应金额的优惠券
$this->couponObj->giveCoupnByLoginTime($subject->loginTimes);
}
}
coupon类
namespace libs\observer;
class Coupon
{
public function giveCoupnByLoginTime($times)
{
if ($times<10) {
echo '送10元优惠券<br/>';
} else if($times>=10 && $times<30) {
echo '送5元优惠券<br/>';
} else {
echo '送3元优惠券<br/>';
}
}
}
5、如果需要其它的观察者如推荐广告,则定义一个用户登录后推广告的观察者对象ObLoginAd,在ObLoginAd里只需要引入Ad类,如果Ad类由其他同事编写,则可以不需要变动他人的代码;
/**
* file name: ObLoginAd.php
* user : 良子
* date : 2021-01-07
*/
namespace libs\observer;
class ObLoginAd implements Observer
{
public function update (Subject $subject)
{
$adObj = new Ad();
//根据用户购买习惯推送相应的商品
$adObj->getDataByCategory($subject->buyCategeory);
}
}
Ad类
/**
* file name: Login.php
* user : 良子
* date : 2021-01-06
*/
namespace libs\observer;
class Ad
{
public function getDataByCategory($categorys)
{
echo '常买品类:'.implode(',', $categorys);
}
}
客户端在调用户登录时的代码
/**
* file name: observer.php
* user : 良子
* date : 2021-01-06
*/
require_once ('./libs/loader/Loader.php');
spl_autoload_register('\\libs\\loader\\Loader::autoload');
$username = '张三';
$passwd = '123456';
$userObj = new \libs\observer\User();
$isLogin = $userObj->login($username, $passwd);
if ($isLogin) { //登录成功
$obLoginCouponObj = new \libs\observer\ObLoginCoupon();
$obLoginAdObj = new \libs\observer\ObLoginAd();
//添加送优惠券的观察者
$userObj->attach($obLoginCouponObj);
//添加广告观察者
$userObj->attach($obLoginAdObj);
//继续添加其它观察者
$userObj->notify();
//具体操作
} else {
//具体操作
}
以上就是一个简单的观察者模式。
大家结合之前所说的单例模式,可以将Coupon和Ad进行改写。让项目更加高效!
关注快乐程序员公众号,每日分享一点小知识。爱编程,爱生活!