在日常生活中,交通信号灯指挥者日益拥挤的城市交通。红灯亮,汽车停止;绿灯亮,汽车继续前行;在这个过程中,交通信号灯是汽车的观察目标,而汽车则是观察者。随着交通信号灯的变化,汽车的行为也会随之变化,一盏交通信号灯可以指挥多辆汽车。在软件系统中,有些对象之间也存在类似交通信号灯和汽车之间的关系,一个对象的状态或行为的变化将会导致其他对象的状态或者行为也发生改变,它们之间将产生联动,正所谓牵一发而动全身。为了更好地描述对象之间存在的这种一对多的联动,观察者模式应运而生。

观察者模式(Chain of Responsibility) 学习难度:★★★☆☆ 使用频率:★★★★★

一、多人联机对战游戏的设计

需求背景:M公司欲开发一款多人联机对战游戏,在游戏中,多个游戏玩家可以加入同一战队组成联盟,当战队中某一成员收到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将作出响应。M公司开发人员需要提供一个设计方案来实现战队成员之间的联动。

  M公司开发人员通过分析,发现在该系统中战队成员之间的联动过程可以简单描述如下:

  联盟成员收到攻击 => 发送通知给盟友 => 盟友作出响应

  如果每个联盟成员都需要持有盟友的信息才能及时通知每一位盟友,因此这样系统开销较大。因此,M公司开发人员决定引入一个新角色“战队控制中心”来负责维护和管理每个战队所有成员的信息,如下图所示:

  如何实现对象之间的联动?让我们来看看观察者模式吧。

二、观察者模式概述

2.1 观察者模式简介

  观察者模式是一种使用频率最高的设计模式之一,用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。

观察者(Observer)模式:定义对象之间的一种一对多依赖关系,使得当每一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。  

2.2 观察者模式结构

  观察者模式包含以下4个角色:

  (1)Subject(抽象目标):又称为主题,是被观察的对象。

  (2)ConcreteSubject(具体目标):抽象目标的子类,通常包含有经常发生改变的数据,当它的状态发生改变时,向其各个观察者发出通知。

  (3)Observer(抽象观察者):观察者将对观察目标的改变做出反应。

  (4)ConcreteObserver(具体观察者):具体观察者中维持一个指向具体目标对象的引用,它用于存储具体观察者的有关状态,这些状态需要和具体目标地状态保持一致。

三、重构多人联机对战游戏

3.1 重构后的设计结构

  其中,AllyControlCenter充当抽象目标,ConcreteAllyControlCenter则充当具体目标,IObserver充当抽象观察者,Player则充当具体观察者。

3.2 具体代码实现

  (1)抽象观察者:IObserver

    /// <summary>
/// 抽象观察类 - IObserver接口
/// </summary>
public interface IObserver
{
string Name { get; set; }
void Help(); // 声明支援盟友的方法
void BeAttacked(AllyControlCenter acc); // 声明遭受攻击的方法
}

  (2)具体观察者:Player

    /// <summary>
/// 具体观察者类:战队成员
/// </summary>
public class Player : IObserver
{
public string Name
{
get;
set;
} public void BeAttacked(AllyControlCenter acc)
{
Console.WriteLine("{0}:我正被攻击,速来援救!", this.Name);
// 调用战队控制中心类的通知方法来通知盟友
acc.NotifyObserver(this.Name);
} public void Help()
{
Console.WriteLine("{0} :坚持住,立马来救你!", this.Name);
}
}

  (3)抽象目标:AllyControlCenter

    /// <summary>
/// 抽象目标类:战队控制中心
/// </summary>
public abstract class AllyControlCenter
{
public string AllyName { get; set; }
protected IList<IObserver> playerList = new List<IObserver>(); public void Join(IObserver observer)
{
playerList.Add(observer);
Console.WriteLine("通知:{0} 加入 {1} 战队", observer.Name, this.AllyName);
} public void Quit(IObserver observer)
{
playerList.Remove(observer);
Console.WriteLine("通知:{0} 退出 {1} 战队", observer.Name, this.AllyName);
} // 声明抽象通知方法
public abstract void NotifyObserver(string name);
}

  (4)具体目标:ConcreteAllyControlCenter

    public class ConcreteAllyControlCenter : AllyControlCenter
{
public ConcreteAllyControlCenter(string allyName)
{
Console.WriteLine("系统通知:{0} 战队组建成功!", this.AllyName);
Console.WriteLine("-------------------------------------------------------");
this.AllyName = allyName;
} // 实现通知方法
public override void NotifyObserver(string playerName)
{
Console.WriteLine("通知:盟友们,{0} 正遭受敌军攻击,速去抢救!", playerName);
foreach (var player in playerList)
{
if (!player.Name.Equals(playerName, StringComparison.OrdinalIgnoreCase))
{
player.Help();
}
}
}
}

  (5)客户端测试

    public class Program
{
public static void Main(string[] args)
{
// Step1.定义观察者对象
AllyControlCenter acc = new ConcreteAllyControlCenter("金庸群侠");
// Step2.定义4个观察者对象
IObserver playerA = new Player() { Name = "杨过" };
acc.Join(playerA);
IObserver playerB = new Player() { Name = "令狐冲" };
acc.Join(playerB);
IObserver playerC = new Player() { Name = "张无忌" };
acc.Join(playerC);
IObserver playerD = new Player() { Name = "段誉" };
acc.Join(playerD);
// Step3.当某盟友遭受攻击
playerA.BeAttacked(acc); Console.ReadKey();
}
}

  编译调试结果如下图所示:

  

  在本实例中,实现了两次对象之间的联动:Player.BeAttacked() => AllyControlCenter.NotifyObserver() => Player.Help()

四、观察者模式与MVC

  在当前流行的MVC(Model-View-Controller)结构中也应用了观察者模式,它包含了3个角色:模型、视图和控制器。其中,模型可对应观察者模式中的观察目标,而视图则对应于观察者,控制器充当二者之间的中介者。当模型层的数据发生改变时,视图将会发出数据请求以期改变其显示内容,如下图所示:

五、观察者模式总结

5.1 主要优点

  (1)可以实现表示层和数据逻辑层的分离 => 各种不同的表示层可以充当具体观察者

  (2)支持广播通信,观察目标会向已注册的观察者对象发送通知 => 简化一对多系统设计的难度

  (3)增加新的观察者无须修改原有系统代码 => 满足开闭原则

5.2 主要缺点

  (1)如果一个观察目标有很多直接和间接的观察者 => 所有观察者收到通知会花费大量时间

  (2)如果观察者和观察目标之间存在循环依赖 => 可能导致系统崩溃

5.3 应用场景

  (1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面 => 封装起来使其独立改变和复用

  (2)一个对象的改变将导致一个或多个其他对象也发生改变,但并不知道具体有多少个对象将要发生改变 => 最熟悉的陌生人

参考资料

  

  刘伟,《设计模式的艺术—软件开发人员内功修炼之道》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

设计模式的征途—15.观察者(Observer)模式的更多相关文章

  1. 设计模式C++描述----04.观察者(Observer)模式

    一. 概述 Observer 模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变. Sbuject 相当于 ...

  2. 《Head First 设计模式》ch.2 观察者(Observer)模式

    观察者模式 定义了对象之间一对多以来,这样一来,当一个对象改变状态时,它所有的依赖者都会收到通知并自动更新 设计原则-松耦合 松耦合将对象之间的互相依赖降到了最低——只要他们之间的接口仍被遵守 观察者 ...

  3. Java 实现观察者(Observer)模式

    1. Java自带的实现 类图 /** * 观察目标 继承自 java.util.Observable * @author stone * */ public class UpdateObservab ...

  4. Java设计模式之从[星际争霸的兵种升级]分析观察者(Observer)模式

    观察者模式定义对象的一种一对多的依赖关系.当一个对象的状态发生改变时.全部依赖于它的对象都会得到通知并被自己主动更新. 一个简单的样例是.在星际争霸的虫族中有一个0基础单位叫做跳狗(Zergling) ...

  5. 观察者(Observer)模式

    观察者模式又叫做发布-订阅模式(Publish.Subscribe)模式.模型-视图模式(Model/View)模式.源-监听器模式(Source/Listener)模式或从属者(Dependents ...

  6. 《图解设计模式》读书笔记8-1 Observer模式

    目录 示例程序 程序类图 程序 角色和类图 角色 类图 思路拓展 可复用性 Observer的顺序 MVC模式 Observer模式 Observer模式即观察者模式,该模式中,被观察者的状态发生变化 ...

  7. 面向对象设计模式——观察者(OBSERVER)模式

    定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.  Observer模式描述了如何建立这种关系.这一模式中的关键对象是目标(subject ...

  8. 设计模式之观察者(OBSERVER)模式

    定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.  Observer模式描述了如何建立这种关系.这一模式中的关键对象是目标(subject ...

  9. Head First 设计模式 —— 02. 观察者 (Observer) 模式

    思考题 在我们的一个实现中,下列哪种说法正确?(多选) P42 public class WeatherDate { // 实例变量声明 public void measurementsChanged ...

随机推荐

  1. 原生API实现拖拽上传文件实践

    功能: 拖拽上传文件.图片,上传的进度条,能够同时上传多个文件. 完整的demo地址:https://github.com/qcer/FE-Components/tree/master/QDrag 涉 ...

  2. J2EE走向成功路-01-Struts2 配置

    配置环境 1.  导入jar包,Struts2官网:http://struts.apache.org/ 2.  创建Action类  继承ActionSupport  (ActionSupport类是 ...

  3. win10 UWP 应用设置

    win10 UWP 应用设置 简单的把设置需要的,放到微软自带的LocalSettings LocalSettings.Values可以存放几乎所有数据 如果需要存放复合数据,一个设置项是由多个值组成 ...

  4. XamlReader动态使用xaml

    xamlload先在xaml做出一个grid,命名xgrid <Page x:Class="xamlload.MainPage" xmlns="http://sch ...

  5. HTML5之window.applicationCache对象

    不知道离线缓存技术的可以参照上一篇文章: HTML5之appcache语法理解/HTML5应用程序缓存/manifest缓存文件官方用法翻译 参考文章 window.applicationCache  ...

  6. php header的使用,PHP常见header状态总结

    <?php//200 正常状态header('HTTP/1.1 200 OK');// 301 永久重定向,记得在后面要加重定向地址 Location:$urlheader('HTTP/1.1 ...

  7. NIO FileChannel

    NIO提供了比传统的文件访问更好的访问方法,NIO有两个优化的方法:一个是 FIleChannel.transferTo FileChannel.transferFrom,另一个是FileChanne ...

  8. [CF] Final Exam Arrangement

    问题链接:http://www.bnuoj.com/v3/contest_show.php?cid=4329#problem/F   问题大意:         就是有1--N们课程,每一个课程都有一 ...

  9. 237. Delete Node in a Linked List(leetcode)

    Write a function to delete a node (except the tail) in a singly linked list, given only access to th ...

  10. sql里的null和空的区别

    null表示为未知,未定义: 空表示为空白,或者0: sql查询,排序时null在''的前面: 定义字段为not null,写为空可以写入: null不可以用来比较,只能用is null判断: