今天我们继续来聊聊Observer 观察者模式 。Observer 观察者模式是新手入门通知类编程解耦合的最佳设计模式,没有之一。观察者模式有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。先来看基本使用用法。
/// <summary>
/// Subject类 主题抽象统治者
/// </summary>
abstract class Subject
{
private List<object> observers=new List<object>();
//增加观察者
public void Attack(Observer observer)
{
observers.Add(observer);
}
//移除观察者
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}
/// <summary>
/// Observer类 抽象观察者 为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口,抽象观察者一般用一个抽象类或者一个接口实现。
/// 更新接口通常包含一个Update()方法
/// </summary>
abstract class Observer
{
public abstract void Update();
}
/// <summary>
/// 具体主题或具体通知者,将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知
/// </summary>
class ConcreteSubject : Subject
{
private string subjectState;
//具体被观察者状态
public string SubjectState
{
get { return subjectState; }
set { subjectState = value; }
}
}
/// <summary>
/// 具体观察者。实现抽象观察者角色需要的更新接口,以便使本身的状态与主题的状态相协调,保存一个指向具体主题对象的引用,
/// 具体观察者角色通常用一个具体子类实现
/// </summary>
class ConcreteObserver : Observer
{
private string name;
private string observerState;
private ConcreteSubject subject;
public ConcreteObserver(ConcreteSubject subject, string name)
{
this.subject = subject;
this.name = name;
}
public override void Update()
{
observerState = subject.SubjectState;
Console.WriteLine("观察者:"+name+"的新状态是:"+observerState);
}
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
首先定义两个抽象类 ,Subject通知者类 它包含一个抽象类 Observer 列表还有3个方法,添加观察者,移除观察者,和通知观察者方法。 一个 抽象Observer观察者类 它包含了一个一个方法 Update() 用来给通知者执行该方法。
接下来是 具体通知者ConcreteSubject类,声明一个string 用来表面自己的名字。还有具体观察者ConcreteObserver类,里面声明一个string名字和一个string状态,一个通知者类。 然后在初始化方法里面把具体通知者传入的类和名字保存到自身引用内。重写了父类的 Update() 方法。输出存放的通知者名称和状态。 这样我们的通知者就可以通知观察者了。最后再来写我们的运行过程:
ConcreteSubject s=new ConcreteSubject();
s.Attack(new ConcreteObserver(s,"X"));
s.Attack(new ConcreteObserver(s, "Y"));
s.Attack(new ConcreteObserver(s, "Z"));
s.SubjectState = "ABC";
s.Notify();
s.SubjectState = "D";
s.Notify();
Console.Read();
首先声明一个具体通知者ConcreteSubject,在添加方法内new 添加3个观察者,分别是xyz。然后声明新状态ABC
,然后下发通知,再改变状态为D,再下发通知。
![](http://img.manew.com/data/attachment/forum/201612/05/164211vmqqomppm6o56uo8.png.thumb.jpg)
我们就可以看到3个观察者xyz分别输出了两次状态。
我们再来做个小例子,可以更好的理解观察者模式的应用场景,比如我们在游戏中,像Boss重生,就会通知战场的全部玩家,又或者游戏中的补给包降落在战场, 我们全部玩家都会收到信息,大量相同的消息分发给大量的人,就可以使用这样的 观察者模式,先来试试:
//通知者接口
interface ISubjects
{
void Attach(Observers o);
void Detach(Observers o);
void Notify();
string SubjectState { get; set; }
}
//观察者
abstract class Observers
{
protected string name;
protected ISubjects sub;
public Observers(string name, ISubjects sub)
{
this.name = name;
this.sub = sub;
}
public abstract void identifyDanger();
public abstract void identifySupy();
}
//具体通知者Boss
class Boss : ISubjects
{
//需要通知的列表
private List<Observers> observers=new List<Observers>();
private string action;
//增加
public void Attach(Observers o)
{
observers.Add(o);
}
//减少
public void Detach(Observers o)
{
observers.Remove(o);
}
public void Notify()
{
foreach (Observers o in observers)
{
o.identifyDanger();
}
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
//具体通知者补给箱
class SupplyBox : ISubjects
{
//需要通知的列表
private List<Observers> observers = new List<Observers>();
private string action;
//增加
public void Attach(Observers o)
{
observers.Add(o);
}
//减少
public void Detach(Observers o)
{
observers.Remove(o);
}
public void Notify()
{
foreach (Observers o in observers)
{
o.identifySupy();
}
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
//具体观察者
class NPCPlayer : Observers
{
public NPCPlayer(string name, ISubjects sub)
: base(name, sub)
{
}
public override void identifyDanger()
{
Console.WriteLine(sub.SubjectState + " " + name + " 收到," + "组建玩家准备攻击");
}
public override void identifySupy()
{
Console.WriteLine(sub.SubjectState + " " + name + " 收到," + "开始前往占领");
}
}
//具体观察者
class EnemyPlayer : Observers
{
public EnemyPlayer(string name, ISubjects sub)
: base(name, sub)
{
}
public override void identifyDanger()
{
Console.WriteLine(sub.SubjectState + " " + name + " 收到," + "联合Boss一起攻击玩家");
}
public override void identifySupy()
{
Console.WriteLine(sub.SubjectState+" " + name + " 收到," + "开始前往争抢");
}
}
//具体观察者
class Player : Observers
{
public Player(string name, ISubjects sub)
: base(name, sub)
{
}
public override void identifyDanger()
{
Console.WriteLine(sub.SubjectState + " " + name + " 收到," + "Boss产生了");
}
public override void identifySupy()
{
Console.WriteLine(sub.SubjectState + " " + name + " 收到," + "地图上出现补给了");
}
}
我们在代码中,首先声明虚拟的通知者类
ISubjects
和观察者类
Observers。 然后是两个具体通知者,一个就是我们说的Boss 和一个补给箱。他们方法类似,
都是添加删除和通知,但是这里不一样的地方是 boss调用的是观察者类的identifyDanger()而补给箱调用的是identifySupy() 用来区分开来,实际的游戏可以改进成传入一个Model类,观察者去解包Model类执行。这样通知者的发消息接口就统一起来了。
然后我们这里分别做了3个观察者,一个是npc,代表友军,一个enemy 代表敌人,还有一个player代表自己。
最后看看运行的代码。
首先声明一个Boss通知者,在new3个观察者,把boss传入。boss通知者把玩家挨个添加起来,然后声明消息内容,通知。
补给包代码也类似,我们就可以看到效果了:
![](http://img.manew.com/data/attachment/forum/201612/05/170240e8w5hw5ouawymhan.png.thumb.jpg)
观察者还可以配合其他模式一起用,如PureMvc就是外观+观察者模式+其他模式组成的MVC架构。该设计模式还有许多可以深入的东西,以后慢慢挖掘。 结尾再来总结下 -Observer 观察者模式的优缺点
优点:观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
缺点:依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。
适用场景:
当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。
一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。