php设计模式入门-观察者模式

场景:一个气象站点,当温度、湿度或气压发生改变时都要像订阅了该气象服务的用户推送提醒,假设用户拥有三种电子产品:mac、iphone和apple watch。

第一版的设计代码如下:


WeatherData.php

<?php
/**
 * 气象站类,用于提供气象数据,数据变化时及时推送至用户的设备
 * 
 * @author ben
 *
 */
class WeatherData{
	/**
	 * 气温
	 * @var string
	 */
	private $_temperature;
	/**
	 * 气压
	 * @var string
	 */
	private $_pressure;
	/**
	 * 湿度
	 * @var string
	 */
	private $_humidity;
	/**
	 * 用户设备之mac
	 * @var object
	 */
	private $_mac;
	/**
	 * 用户设备之iphone
	 * @var object
	 */
	private $_iphone;
	/**
	 * 用户设备之watch(壕啊,咱们做朋友吧。。。)
	 * @var object
	 */
	private $_iwatch;
	
	/**
	 * 初始化用户设备
	 * @param object $mac
	 * @param object $iphone
	 * @param object $iwatch
	 */
	public function __construct($mac, $iphone, $iwatch){
		$this->_mac = $mac;
		$this->_iphone = $iphone;
		$this->_iwatch = $iwatch;
	}
	
	/**
	 * 当数据变更时向用户推送即时数据
	 */
	public function dataChanged(){
		$this->_temperature = $this->getTemperature();
		$this->_humidity = $this->getHumidity();
		$this->_pressure = $this->getPressure();
		
		$this->_mac->update($this->_humidity, $this->_temperature, $this->_pressure);
		$this->_iphone->update($this->_humidity, $this->_temperature, $this->_pressure);
		$this->_iwatch->update($this->_humidity, $this->_temperature, $this->_pressure);
	}
}

代码的坏味道:

气象站类WeatherData与用户的设备类mac、iphone、iwatch类之间存在着强耦合关系,而真实的场景中用户可能随时会取消业务,或者为新的电子产品订阅该业务,这就意味着我们今后将会频繁的修改这个文件中的代码。

下面我们来看看观察者模式是怎么为我们解决这个难题的。

要理解观察者模式,可以结合真实的案例,比如说订阅报纸,仔细想想在这样的场景中存在哪几种元素:

1、报纸的发布者;

2、报纸的订阅者;

发布者与订阅者组成了观察者的两个基本要素。具体用uml类图描述如下:


具体的实现代码:

Subject.php

<?php
abstract class Subject{
	protected $observers;
	public function addObserver($key, Equipment $observer){
		$this->observers[$key] = $observer;
	}
	
	public function removeObserver($key){
		if(isset($this->observers[$key])){
			unset($this->observers[$key]);
		}
	}
	
	abstract function notify();
// 		foreach ($this->observer as $observer){
// 			$observer->update();
// 		}
}

Equipment.php

<?php
abstract class Equipment{
	abstract function update($temperature, $pressure, $humidity);
	abstract function display();
}
WeatherData.php

<?php
require_once 'Subject.php';
class WeatherData extends Subject{
	private $_temperature;
	private $_pressure;
	private $_humidity;
	
	/* (non-PHPdoc)
	 * @see Subject::notify()
	 */
	public function notify() {
		foreach ($this->observers as $observer){
			$observer->update($this->_temperature, $this->_pressure, $this->_humidity);
		}
	}
	
	public function dataChange($temperature, $pressure, $humidity){
		$this->_temperature = $temperature;
		$this->_pressure = $pressure;
		$this->_humidity = $humidity;
		
		$this->notify();
	}
	
	
}

Mac.php

<?php
require_once 'Equipment.php';
class Mac extends Equipment{
	private $_temperature;
	private $_pressure;
	private $_humidity;
	/* (non-PHPdoc)
	 * @see Equipment::update()
	 */
	public function update($temperature, $pressure, $humidity) {
		// TODO Auto-generated method stub
		$this->_temperature = $temperature;
		$this->_pressure = $pressure;
		$this->_humidity = $humidity;
		
		$this->display();
	}
	/* (non-PHPdoc)
	 * @see Equipment::display()
	 */
	public function display() {
		// TODO Auto-generated method stub
		echo "Current conditions:<br />
				temperature:$this->_temperature;<br />
				pressure:$this->_pressure;<br />
				humidity:$this->_humidity;<br />
				    --From Mac client<br />";
	}
}

Iwatch.php

<?php
require_once 'Equipment.php';
class Iwatch extends Equipment{
	private $_temperature;
	private $_pressure;
	private $_humidity;
	/* (non-PHPdoc)
	 * @see Equipment::update()
	 */
	public function update($temperature, $pressure, $humidity) {
		// TODO: Auto-generated method stub
		$this->_temperature = $temperature;
		$this->_pressure = $pressure;
		$this->_humidity = $humidity;
		
		$this->display();
	}
	/* (non-PHPdoc)
	 * @see Equipment::display()
	 */
	public function display() {
		// TODO Auto-generated method stub
		echo "Current conditions:<br />
				temperature:$this->_temperature;<br />
				pressure:$this->_pressure;<br />
				humidity:$this->_humidity;<br />
				    --From Iwatch client<br />";
	}

}
Iphone.php

<?php
require_once 'Equipment.php';
class Iphone extends Equipment{
	private $_temperature;
	private $_pressure;
	private $_humidity;
	/* (non-PHPdoc)
	 * @see Equipment::update()
	 */
	public function update($temperature, $pressure, $humidity) {
		// TODO Auto-generated method stub
		$this->_temperature = $temperature;
		$this->_pressure = $pressure;
		$this->_humidity = $humidity;
		
		$this->display();
	}
	/* (non-PHPdoc)
	 * @see Equipment::display()
	 */
	public function display() {
		// TODO Auto-generated method stub
		echo "Current conditions:<br />
				temperature:$this->_temperature;<br />
				pressure:$this->_pressure;<br />
				humidity:$this->_humidity;<br />
				    --From Iphone client<br />";
	}


	
}
index.php

<?php
require_once 'WeatherData.php';
require_once 'Iwatch.php';
require_once 'Iphone.php';
require_once 'Mac.php';

$subject = new WeatherData();
$observer1 = new Iwatch();
$observer2 = new Iphone();
$observer3 = new Mac();
$subject->addObserver('Iwatch', $observer1);
$subject->addObserver('Iphone', $observer2);
$subject->addObserver('Mac', $observer3);

$subject->dataChange('38摄氏度', '气压好高啊', '很湿润');

运行结果是:

Current conditions:
temperature:38摄氏度;
pressure:气压好高啊;
humidity:很湿润;
    --From Iwatch client
Current conditions:
temperature:38摄氏度;
pressure:气压好高啊;
humidity:很湿润;
    --From Iphone client
Current conditions:
temperature:38摄氏度;
pressure:气压好高啊;
humidity:很湿润;
    --From Mac client


当然php已经帮我们准备好了两个接口,我们没有必要重造轮子,这两个接口分别是SplObserver和SplSubject,关于这两个接口的定义,可以查看以下官方文档:

SplObserver:http://php.net/manual/zh/class.splobserver.php

SplSubject:http://php.net/manual/zh/class.splsubject.php

使用这两个接口之后我们进一步修改后的代码清单如下:

index.php

<?php
require_once 'WeatherData.php';
require_once 'Iwatch.php';
require_once 'Iphone.php';
require_once 'Mac.php';

$subject = new WeatherData();
$observer1 = new Iwatch();
$observer2 = new Iphone();
$observer3 = new Mac();
$subject->attach($observer1);
$subject->attach($observer2);
$subject->attach($observer3);

$subject->dataChange('38摄氏度', '气压好高啊', '很湿润');


Iphone.php

<?php
class Iphone implements SplObserver{
	private $_temperature;
	private $_pressure;
	private $_humidity;
	/* (non-PHPdoc)
	 * @see Equipment::update()
	 */
	public function update(SplSubject $subject) {
		// TODO Auto-generated method stub
		$this->_temperature = $subject->getTemperature();
		$this->_pressure = $subject->getPressure();
		$this->_humidity = $subject->getHumidity();
		
		$this->display();
	}
	/* (non-PHPdoc)
	 * @see Equipment::display()
	 */
	public function display() {
		// TODO Auto-generated method stub
		echo "Current conditions:<br />
				temperature:$this->_temperature;<br />
				pressure:$this->_pressure;<br />
				humidity:$this->_humidity;<br />
				    --From Iphone client<br />";
	}


	
}


Iwatch.php

<?php
class Iwatch implements SplObserver{
	private $_temperature;
	private $_pressure;
	private $_humidity;
	/* (non-PHPdoc)
	 * @see Equipment::update()
	 */
	public function update(SplSubject $subject) {
		// TODO Auto-generated method stub
		$this->_temperature = $subject->getTemperature();
		$this->_pressure = $subject->getPressure();
		$this->_humidity = $subject->getHumidity();
		
		$this->display();
	}
	/* (non-PHPdoc)
	 * @see Equipment::display()
	 */
	public function display() {
		// TODO Auto-generated method stub
		echo "Current conditions:<br />
				temperature:$this->_temperature;<br />
				pressure:$this->_pressure;<br />
				humidity:$this->_humidity;<br />
				    --From Iwatch client<br />";
	}

}



Mac.php

<?php
class Mac implements SplObserver{
	private $_temperature;
	private $_pressure;
	private $_humidity;
	/* (non-PHPdoc)
	 * @see Equipment::update()
	 */
	public function update(SplSubject $subject) {
		// TODO Auto-generated method stub
		$this->_temperature = $subject->getTemperature();
		$this->_pressure = $subject->getPressure();
		$this->_humidity = $subject->getHumidity();
		
		$this->display();
	}
	/* (non-PHPdoc)
	 * @see Equipment::display()
	 */
	public function display() {
		// TODO Auto-generated method stub
		echo "Current conditions:<br />
				temperature:$this->_temperature;<br />
				pressure:$this->_pressure;<br />
				humidity:$this->_humidity;<br />
				    --From Mac client<br />";
	}
}
WeatherData.php

<?php
class WeatherData implements SplSubject{
	protected $observers;
	private $_temperature;
	private $_pressure;
	private $_humidity;
	
	/* (non-PHPdoc)
	 * @see Subject::notify()
	 */
	public function notify() {
		foreach ($this->observers as $observer){
			$observer->update($this);
		}
	}
	
	public function dataChange($temperature, $pressure, $humidity){
		$this->_temperature = $temperature;
		$this->_pressure = $pressure;
		$this->_humidity = $humidity;
		
		$this->notify();
	}
	
	public function getTemperature(){
		return $this->_temperature;
	}
	
	public function getPressure(){
		return $this->_pressure;
	}
	
	public function getHumidity(){
		return $this->_humidity;
	}
	/* (non-PHPdoc)
	 * @see SplSubject::attach()
	 */
	public function attach(SplObserver $observer) {
		// TODO Auto-generated method stub
		$this->observers[] = $observer;		
	}

	/* (non-PHPdoc)
	 * @see SplSubject::detach()
	 */
	public function detach(SplObserver $observer) {
		// TODO Auto-generated method stub
		$key = array_search($observer, $this->observers, true);
		if($key){
			unset($this->observers[$key]);
		}
	}

	
	
}



  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值