设计模式学习总结(五)——观察者模式
一、概要
我们经常会接触到一种情况:一个对象的行为引发其它多个对象相应的行为。这时我们便可以通过观察者模式的设计思想来设计对象模型。
观察者模式又叫发布-订阅(Publish/Subscribe)模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
Java中并没有委托与事件,观察者可以让你更好的理解事件。
二、观察者模式
2.1、观察者设计模式定义
观察者模式(Observer):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
抽象主题(Subject)角色:主题角色把所有对观察考对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般使用一个抽象类或者一个接口实现。
抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update()方法),这个方法叫做更新方法。
具体主题(ConcreteSubject)角色:将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。
具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。
人民日报+订阅者=观察者模式
2.2、实现观察者设计模式
定义:
/// <summary> /// 抽象主题类 /// </summary> public abstract class Subject { private IList<Observer> observers = new List<Observer>(); /// <summary> /// 增加观察者 /// </summary> /// <param name="observer"></param> public void Attach(Observer observer) { observers.Add(observer); } /// <summary> /// 移除观察者 /// </summary> /// <param name="observer"></param> public void Detach(Observer observer) { observers.Remove(observer); } /// <summary> /// 向观察者(们)发出通知 /// </summary> public void Notify() { foreach (Observer o in observers) { o.Update(); } } } /// <summary> /// 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己 /// </summary> public abstract class Observer { public abstract void Update(); } /// <summary> /// 具体观察者或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。 /// </summary> public class ConcreteSubject : Subject { private string subjectState; /// <summary> /// 具体观察者的状态 /// </summary> public string SubjectState { get { return subjectState; } set { subjectState = value; } } } /// <summary> /// 具体观察者,实现抽象观察者角色所要求的更新接口,已是本身状态与主题状态相协调 /// </summary> public class ConcreteObserver : Observer { private string observerState; private string name; private ConcreteSubject subject; /// <summary> /// 具体观察者用一个具体主题来实现 /// </summary> public ConcreteSubject Subject { get { return subject; } set { subject = value; } } public ConcreteObserver(ConcreteSubject subject, string name) { this.subject = subject; this.name = name; } /// <summary> /// 实现抽象观察者中的更新操作 /// </summary> public override void Update() { observerState = subject.SubjectState; Console.WriteLine("The observer's state of {0} is {1}", name, observerState); } }
调用:
class Program { static void Main(string[] args) { // 具体主题角色通常用具体自来来实现 ConcreteSubject subject = new ConcreteSubject(); subject.Attach(new ConcreteObserver(subject, "Observer A")); subject.Attach(new ConcreteObserver(subject, "Observer B")); subject.Attach(new ConcreteObserver(subject, "Observer C")); subject.SubjectState = "Ready"; subject.Notify(); Console.Read(); } }
运行:
2.3、观察者设计模式示例
这里我们使用观察者设计模式实现电热水壶的示例:
主题:水温
观察者:温度计、报警器
抽象观察者Observer :
package DP05; /**抽象观察者*/ public abstract class Observer { /**被通知时的事件*/ public abstract void update(Subject subject); }
抽象主题Subject:
package DP05; import java.util.ArrayList; import java.util.List; /** * 抽象主题 */ public abstract class Subject { //用于存放所有的观察者 private List<Observer> observers = new ArrayList<Observer>(); //添加观察者 public void attach(Observer observer) { observers.add(observer); } //移除观察者 public void tach(Observer observer) { observers.remove(observer); } //通知所有的被观察者 public void notice(Subject subject){ for (Observer obj: observers) { obj.update(subject); } } }
具体主题Boiler水壶:
package DP05; public class Boiler extends Subject { //水温 public int temperature; /**烧水*/ public void onBoil(){ for(int i=0;temperature<100;i++){ temperature=temperature+1; //升温 super.notice(this); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
报警器,实体观察者Alarm:
package DP05; import java.awt.*; /** * 报警器,实体观察者 */ public class Alarm extends Observer { //观察者的反应 @Override public void update(Subject subject) { Boiler boiler = (Boiler) subject; if (boiler.temperature >= 85) { System.out.println("滴滴滴..."); bee(); } } /** * 蜂鸣声音 */ public static void bee() { for (int k = 0; k < 5; k++) { Frame f = new Frame(); Toolkit kit = f.getToolkit(); kit.beep(); try { Thread.sleep(1 * 250); } catch (InterruptedException e) { e.printStackTrace(); } } } }
显示器LED:
package DP05; /** * 显示屏 */ public class LED extends Observer { @Override public void update(Subject subject) { Boiler boiler = (Boiler) subject; System.out.println("当前水温:" + boiler.temperature); } }
测试客户端Client:
package DP05; public class Client { public static void main(String[] args) { //观察者 Observer alarm=new Alarm(); Observer led=new LED(); //主题 Boiler boiler=new Boiler(); boiler.attach(alarm); boiler.attach(led); //烧水 boiler.onBoil(); } }
运行结果:
2.4、改进的观察者
在上一个示例中发现Subject并没有起到明显的作用,客户端完全没有使用,修改后:
主题类:只需要定义一个通知接口,因为不需要知道抽象的观察者,所以不需要添加和移除的方法
具体主题类:实现添加和移除的功能,与具体的观察者的某个行为(方法)相关,而不与具体的观察者对象相关具体观察者
三、总结
优点
观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
缺点
依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。
适用场景
a)、 当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。
b)、一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
设计模式只不过是OOP编程思想的一种体现,并不是一门新的语言,其实现与具体的语言无关(java、c#都可以),只不过各种语言特点不一样,实现手法有所差别
在软件的开发中,不要强行把设计模式运用到软件中,而是要依据需求来决定是否运用设计模式,设计模式不是运用的越多越好
在实际的软件开发中,可能每个设计模式都会与GOF的不太一样,并不强求与GOF的模式一模一样
各种模式是可以混合使用的。并不是独立的。
四、示例
示例:https://coding.net/u/zhangguo5/p/DP01/git
五、视频
视频:https://www.bilibili.com/video/av15867320/
六、作业与资料
作业:
1、根据GOF类型图实现观察者设计模式,要求使用Java
2、南方汽车股份有限公司生产一种公交车,该公交车上搭载了一种速度传感器,速度传感器可以给多种设备提供公交车当前的速度,当速度发生变化时会自动将数据发送给接入的设备且令设备完成自己的工作,如速度仪表会显示当前汽车的速度,语音报警器会根据汽车的速度判断是否超速,并提示驾驶员。通过面向对象分析与设计已得出类图,如下图所示。
3、猫大叫,两只老鼠开始逃跑,主人醒来,宝宝也醒来了并且哭了起来。使用观察者设计模式实现。