观察者模式(监听器模式)
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
优点:
1、观察者和被观察者是抽象耦合的。双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
2、建立一套触发机制。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
类图:
应用实例:
using UnityEngine;
using System.Collections.Generic;
public class TestObserver : MonoBehaviour {
// Use this for initialization
void Start () {
// 具体主题角色通常用具体自来来实现
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();
}
}
/// <summary>
/// 抽象主题类
/// </summary>
public abstract class Subject
{
private List<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.Receive();
}
}
}
/// <summary>
/// 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
/// </summary>
public abstract class Observer
{
public abstract void Receive();
}
/// <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 Receive()
{
observerState = subject.SubjectState;
Debug.LogFormat("The observer's state of {0} is {1}", name, observerState);
}
}
unity应用中:使用C#中的事件委托Delegate来彻底解除通知者和观察者之间的耦合。
关于委托:委托是一种函数指针。一旦为委托分配了方法,委托将与该方法有相同的行为。委托方法可以像其它任何方法一样,具有参数和返回值。委托可以看作是对函数(方法)的的抽象,是特殊的“函数类”,委托的实例代表一个(或多个)具体的函数,它可以是多路广播的。
下面例子分别用观察者模式,委托机制来实现
类图实例描述:客户支付了订单款项,这时财务需要开具发票,出纳需要记账,配送员需要配货。
观察者模式的实现
类图:
代码实现
using UnityEngine;
using System.Collections.Generic;
public class TestSubject : MonoBehaviour {
Customer subject;
// Use this for initialization
void Start () {
subject = new Customer();
subject.Attach(new Accountant(subject));
subject.Attach(new Cashier(subject));
subject.Attach(new Dilliveryman(subject));
}
// Update is called once per frame
void Update () {
subject.Notify();
}
}
/// <summary>
/// 抽象被观察者
/// </summary>
public interface ISubject
{
void Notify();
}
/// <summary>
/// 工作岗位,抽象观察者
/// </summary>
public abstract class JobStation
{
public abstract void Received();
}
/// <summary>
/// 具体客户
/// </summary>
public class Customer : ISubject
{
private List<JobStation> observers = new List<JobStation>();
/// <summary>
/// 增加观察者
/// </summary>
/// <param name="observer"></param>
public void Attach(JobStation observer)
{
this.observers.Add(observer);
}
/// <summary>
/// 移除观察者
/// </summary>
/// <param name="observer"></param>
public void Detach(JobStation observer)
{
this.observers.Remove(observer);
}
public void Notify()
{
foreach (JobStation o in observers)
{
o.Received();
}
}
}
/// <summary>
/// 会计
/// </summary>
public class Accountant : JobStation
{
private Customer customer;
public Accountant(Customer customer)
{
this.customer = customer;
}
/// <summary>
/// 更新状态
/// </summary>
public override void Received()
{
Debug.Log("我是会计,我来开具发票。");
}
}
/// <summary>
/// 出纳
/// </summary>
public class Cashier : JobStation
{
private Customer customer;
public Cashier(Customer customer)
{
this.customer = customer;
}
public override void Received()
{
Debug.Log("我是出纳员,我给登记入账。");
}
}
/// <summary>
/// 快递员
/// </summary>
public class Dilliveryman : JobStation
{
private Customer customer;
public Dilliveryman(Customer customer)
{
this.customer = customer;
}
public override void Received()
{
Debug.Log("我是配送员,我来发货。");
}
}
C#委托实现观察者模式:
类图:
通过类图来看,观察者和主题之间已经不存在任何依赖关系了。
using UnityEngine;
/// 具体被观察者
public class Customer : MonoBehaviour
{
/// 声明委托
public delegate void CustomerEventHandler();
// 创建委托
public static CustomerEventHandler Handler;
//有购买行为,就发送事件
void Update()
{
Handler();
}
}
/// <summary>
/// 财务,已经不需要实现抽象的观察者类,并且不用引用具体的主题
/// </summary>
public class Accountant : MonoBehaviour
{
void Start()
{
// 注册事件
Customer.Handler += GiveInvoice;
}
/// <summary>
/// 开发票
/// </summary>
public void GiveInvoice()
{
Debug.Log("我是会计,我来开具发票。");
}
}
/// <summary>
/// 出纳,已经不需要实现抽象的观察者类,并且不用引用具体的主题
/// </summary>
public class Cashier : MonoBehaviour
{
void Start()
{
// 注册事件
Customer.Handler += Recoded;
}
public void Recoded()
{
Debug.Log("我是出纳员,我给登记入账。");
}
}
/// <summary>
/// 配送员,已经不需要实现抽象的观察者类,并且不用引用具体的主题
/// </summary>
public class Dilliveryman : MonoBehaviour
{
void Start()
{
// 注册事件
Customer.Handler += Dilliver;
}
public void Dilliver()
{
Debug.Log("我是配送员,我来发货。");
}
}
参考:http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html
参考:http://www.runoob.com/design-pattern/design-pattern-tutorial.html