设计模式的艺术之道--观察者模式

来源:互联网 发布:linux工具链 编辑:程序博客网 时间:2024/05/24 23:15

设计模式的艺术之道–观察者模式

声明:本系列为刘伟老师博客内容总结(http://blog.csdn.net/lovelion),博客中有完整的设计模式的相关博文,以及作者的出版书籍推荐

本系列内容思路分析借鉴了刘伟老师的博文内容,同时改用C#代码进行代码的演示和分析(Java资料过多 C#表示默哀).

本系列全部源码均在文末地址给出。


本系列开始讲解行为型模式,关注如何将现有类或对象组织在一起形成更加强大的结构。

  • 行为型模式(Behavioral Pattern)
    关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信与协作,进一步明确对象的职责
    不仅仅关注类和对象本身,还重点关注它们之间的相互作用和职责划分
  • 类行为型模式
    使用继承关系在几个类之间分配行为,主要通过多态等方式来分配父类与子类的职责
  • 对象行为型模式
    使用对象的关联关系来分配行为,主要通过对象关联等方式来分配两个或多个类的职责

11种常见的行为型模式
这里写图片描述


观察者模式–对象间的联动

“红灯停,绿灯行”,在这个过程中,交通信号灯是汽车的观察目标,而汽车是观察者。随着交通信号灯的变化,汽车的行为也将随之而变化,一盏交通信号灯可以指挥多辆汽车。
在软件系统中,有些对象之间也存在类似交通信号灯和汽车之间的关系,一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动,正所谓“触一而牵百发”。
这里写图片描述

1.1定义

-观察者模式 (ObserverPattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。
- 发生改变的对象称为观察目标,被通知的对象称为观察者
- 一个观察目标可以对应多个观察者
别称: 发布-订阅(Publish/Subscribe)模式

1.2情景实例

问题描述
- 多人联机对战
在某多人联机对战游戏中,多个玩家可以加入同一战队组成联盟,当战队中的某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将做出响应。

初步思路分析
开发人员决定引入一个新的角色——“战队控制中心”——来负责维护和管理每个战队所有成员的信息。当一个联盟成员受到攻击时,将向相应的战队控制中心发送求助信息,战队控制中心再逐一通知每个盟友,盟友再作出响应。
AllyControlCenter充当目标类,ConcreteAllyControlCenter充当具体目标类,Observer充当抽象观察者,Player充当具体观察者。
UML类图
这里写图片描述
实例关键源代码

 abstract class AllyControlCenter    {        protected string allyName; //战队名称        protected List<IObserver> players = new List<IObserver>(); //定义一个集合用于存储战队成员        public void SetAllyName(string allyName)        {            this.allyName = allyName;        }        public string GetAllyName()        {            return this.allyName;        }        //注册方法        public void Join(IObserver obs)         {            Console.WriteLine("{0}加入{1}战队!", obs.Name, this.allyName);            players.Add(obs);        }        //注销方法        public void Quit(IObserver obs)         {            Console.WriteLine("{0}退出{1}战队!", obs.Name, this.allyName);            players.Remove(obs);        }        //声明抽象通知方法        public abstract void NotifyObserver(string name);    }      class ConcreteAllyControlCenter : AllyControlCenter    {        public ConcreteAllyControlCenter(string allyName)         {            Console.WriteLine("{0}战队组建成功!", allyName);            Console.WriteLine("----------------------------");            this.allyName = allyName;        }        //实现通知方法        public override void NotifyObserver(string name)         {            Console.WriteLine("{0}战队紧急通知,盟友{1}遭受敌人攻击!", this.allyName, name);            //遍历观察者集合,调用每一个盟友(自己除外)的支援方法            foreach(object obs in players)             {                if (!((IObserver)obs).Name.Equals(name))                 {                    ((IObserver)obs).Help();                }            }               }        interface IObserver        {          string Name          {              get;              set;          }          void Help(); //声明支援盟友方法          void BeAttacked(AllyControlCenter acc); //声明遭受攻击方法        }         class Player : IObserver    {        private string name;        public Player(string name)        {            this.name = name;        }        public string Name        {            get { return name; }            set { name = value; }        }        //支援盟友方法的实现        public void Help()        {            Console.WriteLine("坚持住,{0}来救你!", this.name);        }        //遭受攻击方法的实现,当遭受攻击时将调用战队控制中心类的通知方法NotifyObserver()来通知盟友        public void BeAttacked(AllyControlCenter acc)         {            Console.WriteLine("{0}被攻击!", this.name);            acc.NotifyObserver(name);               }    }     class Program    {        static void Main(string[] args)        {            //定义观察目标对象            AllyControlCenter acc;            acc = new ConcreteAllyControlCenter("金庸群侠");            //定义四个观察者对象            IObserver player1, player2, player3, player4;            player1 = new Player("杨过");            acc.Join(player1);            player2 = new Player("令狐冲");            acc.Join(player2);            player3 = new Player("张无忌");            acc.Join(player3);            player4 = new Player("段誉");            acc.Join(player4);            //某成员遭受攻击            player1.BeAttacked(acc);            Console.Read();        }    }}

C#中的委托和事件
C#事件注册方法语法:
eventSource.someEvent += new SomeEventHandler(someMethod);
eventSource表示事件源,someEvent表示定义在事件源中的事件,SomeEventHandler表示用于处理事件的委托,someMethod表示与委托SomeEventHandler具有相同函数签名的事件处理方法
只需要修改someMethod,即可实现相同的事件对应不同的事件处理程序
.NET中的事件处理模型是观察者模式的一种变形,它与观察者模式的实现原理本质上是一致的。
代码案例:

namespace ObserverExtend{    class EventTest    {        //定义一个委托        public delegate void UserInput(object sender, EventArgs e);        //定义一个此委托类型的事件        public event UserInput OnUserInput;        //模拟事件触发,当输入“0”时引发事件        public void Method()        {            bool flag = false;            Console.WriteLine("请输入数字:");            while (!flag)            {                if (Console.ReadLine() == "0")                {                    OnUserInput(this, new EventArgs());                }            }        }    }     class Program    {        public Program(EventTest test)        {            //注册事件或订阅事件            test.OnUserInput += new EventTest.UserInput(Handler);            test.OnUserInput += new EventTest.UserInput(HandlerMore);            //注销事件或取消订阅            //test.OnUserInput -= new EventTest.UserInput(Handler);        }        public void Handler(object sender, EventArgs e)        {            Console.WriteLine("数据输入结束!");        }        public void HandlerMore(object sender, EventArgs e)        {            Console.WriteLine("真的结束了!");        }        static void Main(string[] args)        {            EventTest test = new EventTest();            Program program = new Program(test);            test.Method();        }    }}

1.3模式分析

动机和意图

  • 一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动,应该如何设计关系的解耦?

一般结构

  • 观察者模式包含4个角色:
  • Subject(目标):指被观察的对象。在目标中定义了一个观察者集合,并有增加和删除观察者的方法,同时它定义了通知方法notify()。
  • ConcreteSubject(具体目标):通常包含经常发生改变的数据,改变时向它的各个观察者发出通知。(上边的战队中心)
  • Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
  • ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,实现抽象观察者Observer中定义的update()方法。(每一个战队成员)

    观察者模式UML类图
    这里写图片描述

改进后的优点

  • 在观察目标和观察者之间建立一个抽象的耦合
  • 支持广播通信,简化了一对多系统设计的难度
  • 符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下

现存的缺点

  • 将所有的观察者都通知到会花费很多时间
  • 如果存在循环依赖时可能导致系统崩溃
  • 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的

适用场景
(1) 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。
(2) 一个对象的改变将导致一个或多个其他对象发生改变,且并不知道具体有多少对象将发生改变,也不知道这些对象是谁。
(3) 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
举例:B站Up主的订阅机制,每当Up主更新,订阅的粉丝即可收到通知信息。
大话西游帮派,帮主的命令发布,山贼来袭,全军出击,帮众都跑进来参加活动。
观察者模式是比较常用的一种模式,务必要理解。
观察者模式与MVC
MVC(Model-View-Controller)架构中也应用了观察者模式,MVC是一种架构模式。
它包含三个角色:模型(Model),视图(View)和控制器(Controller)。
模型可对应于观察者模式中的观察目标,而视图对应于观察者,控制器可充当两者之间的中介者
当模型层的数据发生改变时,视图层将自动改变其显示内容。
这里写图片描述
模型层提供的数据是视图层所观察的对象,在视图层中包含两个用于显示数据的图表对象,一个是柱状图,一个是饼状图,相同的数据拥有不同的图表显示方式,如果模型层的数据发生改变,两个图表对象将随之发生变化,这意味着图表对象依赖模型层提供的数据对象,因此数据对象的任何状态改变都应立即通知它们。同时,这两个图表之间相互独立,不存在任何联系,而且图表对象的个数没有任何限制,用户可以根据需要再增加新的图表对象,如折线图。在增加新的图表对象时,无须修改原有类库,满足“开闭原则”
实例源代码
GitHub地址
百度云地址:链接: https://pan.baidu.com/s/1pLT2WzD 密码: 8nmr

原创粉丝点击