一、定义
观察者模式(Observer Pattern)定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫发布-订阅模式、模型-视图模式、源-监听模式或从属者模式。观察者模式是一种对象行为型模式。
二、模式结构
观察者模式包含如下角色。
1.Subject(目标)
目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,它可以存储任意数量的观察者对象,它提供一个接口来增加和删除观察者对象,同时它定义了的通知方法notify()。目标类可以是接口,也可以是抽象类或实现类。
2.ConcreteSubject(具体目标)
具体目标是目标类的子类,通常它包含经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知。同时它还实现了在目标类中定义的抽象业务逻辑方法。
3.Observer(观察者)
观察者将对观察目标的改变作出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
4.ConcreteObserver(具体观察者)
在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法。通常在实现时,可以调用具体目标类的attach()方法将自己添加到目标类的观察者集合中或通过detach()方法将自己从目标类的观察者集合中删除。
三、优缺点
1.优点
(1)实现了表示层和数据逻辑层的分离
(2)在观察目标和观察者之间建立一个抽象的耦合
(3)观察者模式支持广播通信,观察目标会向所有注册的观察者发出通知,简化了一对多系统设计的难度
(4)符合“开闭原则”
2.缺点
(1)如果一个观察目标对象有很多直接或间接的观察者的话,将所有的观察者都通知到会花费很多时间
(2)如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
(3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
四、适用环境
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面
(2)一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
(3)一个对象必须通知其他对象,而不知道这些对象是谁。
五、实例
在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将做出响应。
现使用观察者模式设计并实现该过程,以实现战队成员之间的联动。
类图:
<?php
// 抽象观察者类
interface Observer
{
public function getName();
public function setName($name);
public function help();//声明支援盟友方法
public function beAttacked(AllyControlCenter $allyControlCenter);//声明遭受攻击方法
}
<?php
// 战队成员类(具体观察者类)
require_once 'Observer.php';
require_once 'AllyControlCenter.php';
class player implements Observer
{
private $name;
public function __construct($name)
{
$this->name=$name;
}
public function setName($name)
{
// TODO: Implement setName() method.
$this->name=$name;
}
public function getName()
{
// TODO: Implement getName() method.
return $this->name;
}
//支援盟友方法
public function help()
{
// TODO: Implement help() method.
echo '坚持住,'.$this->name.'来救你啦'.PHP_EOL;
}
//遭受攻击方法
public function beAttacked(AllyControlCenter $allyControlCenter)
{
// TODO: Implement beAttacked() method.
echo $this->name.'被攻击'.PHP_EOL;
$allyControlCenter->notify($this->name);
}
}
<?php
//抽象目标类(指挥部类)
require_once 'Observer.php';
abstract class AllyControlCenter
{
protected $allyName;//战队名称
protected $arrayList=[];//定义一个用于存储战队成员的集合
public function setAllyName($allyName)
{
$this->allyName=$allyName;
}
public function getAllyName(){
return $this->allyName;
}
//注册方法
public function join(Observer $obs){
echo $obs->getName().'加入'.$this->allyName.'战队'.PHP_EOL;
array_push($this->arrayList, $obs);
}
//注销方法
public function quit(Observer $obs){
echo $obs->getName().'退出'.$this->allyName.'战队'.PHP_EOL;
// array_shift($this->arrayList);
$key=array_search($obs,$this->arrayList);//查找对应的键值
array_splice($this->arrayList,$key,1);//删除注销用户
$this->lookPlayer();
}
//查看战队成员
public function lookPlayer(){
echo '战队成员:';
foreach ($this->arrayList as $value){
echo $value->getName().' ';
}
echo PHP_EOL;
}
//通知方法
public abstract function notify($name);
}
<?php
// 具体目标类
require_once 'AllyControlCenter.php';
require_once 'Observer.php';
class ConcreteAllyControlCenter extends AllyControlCenter
{
public function __construct($allyName)
{
echo $allyName.'战队组建成功'.PHP_EOL;
$this->allyName=$allyName;
}
//通知盟友
public function notify($name)
{
// TODO: Implement notify() method.
echo $this->allyName.'战队紧急通知,盟友'.$name.'遭受敌人攻击'.PHP_EOL;
foreach ($this->arrayList as $value){
if($value->getName()!=$name){
$value->help();
}
}
}
}
<?php
//客户端
require_once 'player.php';
require_once 'ConcreteAllyControlCenter.php';
//创建战队
$ally=new ConcreteAllyControlCenter('金庸群侠');
//创建用户
$player1=new player('杨过');
$ally->join($player1);
$player2=new player('小龙女');
$ally->join($player2);
$player1->beAttacked($ally);//受到攻击
$ally->lookPlayer();//查看战队成员
$ally->quit($player2);//退出战队
效果: