jtahstu的博客

1373758426   root@jtahstu.com   Git仓库   英文博客  

最新碎语:以后没事写写小的知识点吧

您的位置:jtahstu的博客 >笔记> 设计模式从入门到放弃 - 观察者模式

设计模式从入门到放弃 - 观察者模式

观察者模式定义

    观察者模式定义了一种一对多的依赖关系让多个观察者对象同时监听某一个主题对象这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己。

观察者模式结构与分析

   

    抽象主题角色(Subject):把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。

    抽象观察者角色(Observer):为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。

    具体主题角色(ConcreteSubject):在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。

    具体观察者角色(ConcreteObserver):该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。

观察者模式代码演示

//Subject.java
import java.util.Vector;

public abstract class Subject {
	// 定一个一个观察者数组
	private Vector<Observer> obsVector = new Vector<Observer>();

	// 增加一个观察者
	public void addObserver(Observer o) {
		this.obsVector.add(o);
	}

	// 删除一个观察者
	public void delObserver(Observer o) {
		this.obsVector.remove(o);
	}

	// 通知所有观察者
	public void notifyObserver() {
		for (Observer o : this.obsVector) {
			o.update();
		}
	}
}

//ConcreteSubject.java
public class ConcreteSubject extends Subject {

	// 具体的业务
	public void doSomething() {
		/*
		 * do something
		 */
		super.notifyObserver();
	}
}

//Observer.java
public interface Observer {

	// 更新方法
	public void update();
}

//ConcreteObserver.java
public class ConcreteObserver implements Observer {

	// 实现更新方法
	public void update() {
		System.out.println("接收到信息,并进行处理!");
	}

}

//Client.java
public class Client {

	public static void main(String[] args) {
		// 创建一个被观察者
		ConcreteSubject subject = new ConcreteSubject();
		// 定义一个观察则
		Observer obs = new ConcreteObserver();
		// 观察者观察被被观察则
		subject.addObserver(obs);
		// 观察者开始活动了
		subject.doSomething();
	}

}
观察者模式实例UML图


观察者模式实例Java演示代码

//MySubject.java
import java.util.ArrayList;

public abstract class MySubject {
	protected ArrayList observers = new ArrayList();

	// 注册方法
	public void attach(MyObserver observer) {
		observers.add(observer);
	}

	// 注销方法
	public void detach(MyObserver observer) {
		observers.remove(observer);
	}

	public abstract void cry(); // 抽象通知方法
}

//MyObserver.java
public interface MyObserver {
	void response(); // 抽象响应方法
}

//Cat.java
public class Cat extends MySubject {
	public void cry() {
		System.out.println("猫叫!");
		System.out.println("----------------------------");

		for (Object obs : observers) {
			((MyObserver) obs).response();
		}

	}
}

//Mouse.java
public class Mouse implements MyObserver {
	public void response() {
		System.out.println("老鼠努力逃跑!");
	}
}

//Dog.java
public class Dog implements MyObserver {
	public void response() {
		System.out.println("狗跟着叫!");
	}
}

//Client.java
public class Client {
	public static void main(String a[]) {
		MySubject subject = new Cat();

		MyObserver obs1, obs2, obs3;
		obs1 = new Mouse();
		obs2 = new Mouse();
		obs3 = new Dog();

		subject.attach(obs1);
		subject.attach(obs2);
		subject.attach(obs3);

		subject.cry();
	}
}

PHP实例演示

<?php
/**
 * 定义观察接口
 */
interface Subject {
	public function Attach($Observer); //添加观察者
	public function Detach($Observer); //踢出观察者
	public function Notify(); //满足条件时通知观察者
	public function SubjectState($Subject); //观察条件
}

/**
 * 观察类的具体实现
 */
class Boss Implements Subject {
	public $_action;

	private $_Observer;

	public function Attach($Observer) {
		$this -> _Observer[] = $Observer;
	}

	public function Detach($Observer) {
		$ObserverKey = array_search($Observer, $this -> _Observer);

		if ($ObserverKey !== false) {
			unset($this -> _Observer[$ObserverKey]);
		}
	}

	public function Notify() {
		foreach ($this->_Observer as $value) {
			$value -> Update();
		}
	}

	public function SubjectState($Subject) {
		$this -> _action = $Subject;
	}

}

/**
 * 抽象观察者
 *
 */
abstract class Observer {
	protected $_UserName;

	protected $_Sub;

	public function __construct($Name, $Sub) {
		$this -> _UserName = $Name;
		$this -> _Sub = $Sub;
	}

	public abstract function Update(); //接收通过方法
}

/**
 * 观察者
 */
class StockObserver extends Observer {
	public function __construct($name, $sub) {
		parent::__construct($name, $sub);
	}

	public function Update() {
		echo $this -> _Sub -> _action . $this -> _UserName . " 你赶快跑...";
	}

}

$huhansan = new Boss();	//被观察者
$gongshil = new StockObserver("三毛", $huhansan);	//初始化观察者

$huhansan -> Attach($gongshil);	//添加一个观察者
$huhansan -> Attach($gongshil);	//添加一个相同的观察者
$huhansan -> Detach($gongshil);	//踢出基中一个观察者

$huhansan -> SubjectState("警察来了");	//达到满足的条件
$huhansan -> Notify();	//通过所有有效的观察者

PHP实例演示2.0

<?php
class car implements SplSubject {
	//车的类型
	private $carName;
	//车的状态,0为关闭,1这启动车子
	private $carState = 0;
	//初始化车的速度表值
	private $carSpeed = 0;
	//各项车的性能观察对象
	private $Observers;
	public function __construct($Name) {
		$this -> carName = $Name;
		$this -> Observers = new SplObjectStorage;
	}

	//启动
	public function start() {
		$this -> carState = 1;
		$this -> notify();
	}

	//停车
	public function stop() {
		$this -> carState = 0;
		$this -> carSpeed = 0;
		$this -> notify();
	}

	//加速
	public function accelerate($Acceleration) {
		if (0 === $this -> carState) {
			throw new Exception('先踩油门,不然车怎走啊!!!');
		}
		if (!is_int($Acceleration) || $Acceleration < 0) {
			throw new Exception('加速值错了啊');
		}
		$this -> carSpeed += $Acceleration;
		$this -> notify();
	}

	//增加监测对象
	public function attach(SplObserver $observer) {
		if (!$this -> Observers -> contains($observer)) {
			$this -> Observers -> attach($observer);
		}
		return true;
	}

	//删除监测对象
	public function detach(SplObserver $observer) {
		if (!$this -> Observers -> contains($observer)) {
			return false;
		}
		$this -> Observers -> detach($observer);
		return true;
	}

	//传送对象
	public function notify() {
		foreach ($this->Observers as $observer) {
			$observer -> update($this);
		}
	}

	public function __get($Prop) {
		switch ($Prop) {
			case 'STATE' :
				return $this -> carState;
				break;
			case 'SPEED' :
				return $this -> carSpeed;
				break;
			case 'NAME' :
				return $this -> carName;
				break;
			default :
				throw new Exception($Prop . 'cannotberead');
		}
	}

	public function __set($Prop, $Val) {
		throw new Exception($Prop . 'cannotbeset');
	}

}

class carStateObserver implements SplObserver {
	private $SubjectState;
	public function update(SplSubject $subject) {
		switch ($subject->STATE) {
			case 0 :
				if (is_null($this -> SubjectState)) {
					echo $subject -> NAME . '没有启动呢' . "\t";
				} else {
					echo $subject -> NAME . '熄火了' . "\t";
				}
				$this -> SubjectState = 0;
				break;
			case 1 :
				if (1 !== $this -> SubjectState) {
					echo $subject -> NAME . '启动了' . "\t";
					$this -> SubjectState = 1;
				}
				break;
			default :
				throw new Exception('UnexpectederrorincarStateObserver::update()');
		}
	}

}

class carSpeedObserver implements SplObserver {
	public function update(SplSubject $subject) {
		if (0 !== $subject -> STATE) {
			echo $subject -> NAME . '目前速度为' . $subject -> SPEED . 'Kmh' . "\t";
		}
	}

}

class carOverspeedObserver implements SplObserver {
	public function update(SplSubject $subject) {
		if ($subject -> SPEED > 130) {
			throw new Exception('加速限制在130以内,你违规了!' . "\t");
		}
	}

}

try {
	$driver = new car('AUDIA4');
	$driverObserver1 = new carStateObserver;
	$driverObserver2 = new carSpeedObserver;
	$drivesrObserver3 = new carOverspeedObserver;
	$driver -> attach($driverObserver1);
	$driver -> attach($driverObserver2);
	$driver -> attach($drivesrObserver3);
	$driver -> start();
	$driver -> accelerate(10);
	$driver -> accelerate(30);
	$driver -> stop();
	$driver -> start();
	$driver -> accelerate(50);
	$driver -> accelerate(70);
	$driver -> accelerate(100);
	$driver -> accelerate(150);
} catch (Exception $e) {
	echo $e -> getMessage();
}
?>

观察者模式的优缺点

优点:

观察者模式实现了观察者和目标之间的抽象耦合
            原本目标对象在状态发生改变的时候,需要直接调用所有的观察者对象,但是抽象出观察者接口过后,目标和观察者就只是在抽象层面上耦合了,也就是说目标只是知道观察者接口,并不知道具体的观察者的类,从而实现目标类和具体的观察者类之间解耦。

观察者模式实现了动态联动
            所谓联动,就是做一个操作会引起其它相关的操作。由于观察者模式对观察者注册实行管理,那就可以在运行期间,通过动态的控制注册的观察者,来控制某个动作的联动范围,从而实现动态联动。

观察者模式支持广播通信
            由于目标发送通知给观察者是面向所有注册的观察者,所以每次目标通知的信息就要对所有注册的观察者进行广播。当然,也可以通过在目标上添加新的功能来限制广播的范围。

缺点:

    在广播通信的时候要注意一个问题,就是相互广播造成死循环的问题。比如A和B两个对象互为观察者和目标对象,A对象发生状态变化,然后A来广播信息,B对 象接收到通知后,在处理过程中,使得B对象的状态也发生了改变,然后B来广播信息,然后A对象接到通知后,又触发广播信息……,如此A引起B变化,B又引 起A变化,从而一直相互广播信息,就造成死循环了。

    观察者模式可能会引起无谓的操作,由于观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法,如果观察者不需要执行相应处理,那么这次操作就浪费了。其实浪费了还好,怕就怕引起了误更新,那就麻烦了,比如:本应该在执行这次状态更新前把某个观察者删除掉,这样通知的时候就没有这个观察者了,但是现在忘掉了,那么就会引起误操作。

    如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知会花费很多的时间。

    观察者模式没有相应的机制让观察者知道所观察者的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

观察者模式的本质

    观察者模式的本质:触发联动

    当修改目标对象的状态的时候,就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法,其实就相当于联动调用这些观察者的方法。而且这个联动还是动态的,可以通过注册和取消注册来控制观察者,因而可以在程序运行期间,通过动态的控制观察者,来变相的实现添加和删除某些功能处理,这些功能就是观察者在update的时候执行的功能。

    同时目标对象和观察者对象的解耦,又保证了无论观察者发生怎样的变化,目标对象总是能够正确地联动过来。

    理解这个本质对我们非常有用,对于我们识别和使用观察者模式有非常重要的意义,尤其是在变形使用的时候,万变不离其宗。

观察者模式适用环境

    当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,那么就可以选用观察者模式,将这两者封装成观察者和目标对象,当目标对象变化的时候,依赖于它的观察者对象也会发生相应的变化。这样就把抽象模型的这两个方面分离开了,使得它们可以独立的改变和复用。    

    如果在更改一个对象的时候,需要同时连带改变其它的对象,而且不知道究竟应该有多少对象需要被连带改变,这种情况可以选用观察者模式,被更改的那一个对象很明显就相当于是目标对象,而需要连带修改的多个其它对象,就作为多个观察者对象了。   

    当一个对象必须通知其它的对象,但是你又希望这个对象和其它被它通知的对象是松散耦合的,也就是说这个对象其实不想知道具体被通知的对象,这种情况可以选用观察者模式,这个对象就相当于是目标对象,而被它通知的对象就是观察者对象了

  • 参考

  1. 百度百科 http://baike.baidu.com/view/1854779.htm
  2. ITeye http://canann.iteye.com/blog/1814653
  3. PHP点点通 http://www.phpddt.com/php/observer.html
  4. 课本
  5. 《设计模式之禅》

---

本文章采用 知识共享署名2.5中国大陆许可协议 进行许可,欢迎转载,演绎或用于商业目的。

---

二维码加载中...

扫一扫移动端访问O(∩_∩)O

发表评论

22 + 85 =
路人甲 表情
看不清楚?点图切换 Ctrl+Enter快速提交
正在加载中……