1.观察者模式(Observer/Event)
1.动机
- 在软件构建过程中,需要为某些对象建立一种“通知依赖关系”——一个对象(目标)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
- 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系,从而实现软件体系结构的松耦合。
2.模式定义
- 定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的所有对象都得到通知并自动更新。——《设计模式》GoF
3.结构图
4.模式的组成
有上面结构图可以看出,在观察者模式的结构图有一下角色:
- 抽象主题角色(Subject):抽象主题把所有观察者对象的引用保存在一个列表中,并提供增加和删除观察者对象的操作,抽象主题角色又叫做抽象被观察者角色,一般由抽象类或接口实现。
- 抽象观察者角色(Observer):为所有具体观察者定义一个接口,在得到主题通知时更新自己,一般由抽象类或接口实现。
- 具体主题角色(ConcerteSubject):实现抽象主题接口,具体主题角色又叫做具体被观察者角色。
- 具体观察者角色(ConcreteObserver):实现抽象观察者角色所要求的的接口,以便使自身转台与主题的状态相协调。
2.例子实现
1.描述
订阅银行短信业务,当我们账户余额发生改变,我们就会收到相应的短信。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pattern
{
//抽象观察者
public abstract class abstractDepositor
{
private string name;//账户唯一的ID
private int balance;//账户余额
private bool moneyChanged = false;//账户余额未发生变化
private DateTime datetime;//账户余额发生变化的时间
/// <summary>
/// 初始化
/// </summary>
/// <param name="name"></param>
/// <param name="total"></param>
public abstractDepositor(string name,int total)
{
this.name = name;
this.balance = total;
}
/// <summary>
/// 取钱
/// </summary>
/// <param name="money"></param>
public void GetMoney(int money)
{
this.balance = this.balance - money;
this.datetime = DateTime.Now;
this.moneyChanged = true;
}
public int _balance
{
get { return balance; }
set
{
balance = value;
}
}
public bool _IsChanged
{
get { return moneyChanged; }
set { moneyChanged = value; }
}
public DateTime _datetime
{
get{return datetime;}
set{datetime=value;}
}
public string _name
{
get { return name; }
set { name = value; }
}
//更新账户状态
public abstract void UpdateMoney(int money,DateTime datetime);
}
//抽象通知机制,支持多个观察者,在这里Subject和ConcreteSubject可以写一块,也可以抽取出来。
public class Subject
{
public List<abstractDepositor> observers = new List<abstractDepositor>();
//添加储户
public void _add(abstractDepositor observer)
{
if(!observers.Exists(x=>x._name==observer._name))
observers.Add(observer);
}
//删除储户
public void _remove(abstractDepositor observer)
{
if (observers.Exists(x => x._name == observer._name))
observers.Remove(observer);
}
//通知储户账户变化情况
public void onNotify()
{
foreach (abstractDepositor observer in observers)
{
if(observer._IsChanged)
{
observer.UpdateMoney(observer._balance,observer._datetime);
}
}
}
}
public class Person1 : abstractDepositor
{
public Person1(string name,int total):base (name,total){}
public override void UpdateMoney(int currentbalance,DateTime datetime)
{
Console.WriteLine("{0}的账户余额在{1}的时候发生变化,剩余余额为:{2}",this._name,datetime,currentbalance);
}
}
public class Person2 : abstractDepositor
{
public Person2(string name, int total) : base(name, total) { }
public override void UpdateMoney(int currentbalance,DateTime datetime)
{
Console.WriteLine("您的账户余额在{0}的时候发生变化,剩余余额为:{1}", datetime,currentbalance);
}
}
class Program
{
static void Main(string[] args)
{
Subject subject = new Subject();
Person1 person1 = new Person1("dangxiaochun",50000);
Person2 person2 = new Person2("zhumiao",300000);
subject._add(person1);
subject._add(person2);
person1.GetMoney(5000);
subject.onNotify();
person1.GetMoney(300);
person2.GetMoney(400);
subject.onNotify();
Console.ReadKey();
}
}
}
3.要点总结
- 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达至松耦合。
- 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
- 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
- Observer模式是基于事件的UI框架中常常用的设计模式,也是MVC模式的一个重要组成部分
优点:
(1)、观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层,即观察者。
(2)、观察者模式在被观察者和观察者之间建立了一个抽象的耦合,被观察者并不知道任何一个具体的观察者,只是保存着抽象观察者的列表,每个具体观察者都符合一个抽象观察者的接口。
(3)、观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知。
缺点:
(1)、如果一个被观察者有很多直接和间接的观察者时,将所有的观察者都通知到会花费很多时间。
(2)、虽然观察者模式可以随时使观察者知道所观察的对象发送了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎样发生变化的。
(3)、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃,在使用观察者模式应特别注意这点。