观察者模式允许我们观察应用程序中一个对象的状态。最好的理解它的方式就是 Publish/ Subscribe 模式。
在观察者模式中有被观察的叫subject,一些观察subject 的对象叫观察者。该模式是松耦合的一个非常好的实例
因为我们的类可以依赖很少的信息进行交互。
subject 需要被三件事关注:
1. 注册一个观察者
2. 删除一个观察者
3. 通知事件的观察者
比方说我们准备创建一个video game, 在我们的游戏中,我们有一些非玩家角色,这些角色依赖于玩家。
为了该需要,我们假设我们有一个弓箭手类和一个击剑手类,如果玩家在范围内,弓箭手类能够进行攻击,
如果玩家已经被关闭,那么击剑手类就能够进行攻击。那么,我们如何应用观察者模式实现这样的控制呢。
这是一个很好的应用观察者模式的例子。 在这里 玩家就是 subject . 玩家没有任何概念关于有多少非玩家角色在
观察它。有意思是这都没有关系,因为我们正在广播玩家的位置给关心玩家的非玩家角色。
这里我们采用的是 2维模式。因为计算两点间的距离比较容易。
为了实现观察者模式,我们先创建两个接口,第一个为subject. Subject 需要能够注册观,删除,通知 观察者。
在这个实例中,观察者要基于subject (玩家) 的位置去尝试进行攻击。下面是定义的接口。
{
public interface ISubject
{
void RegisterObserver(IObserver observer);
void UnregisterObserver(IObserver observer);
void NofifyObservers();
}
public interface IObserver
{
void Attack(ref Position p);
}
}
我们需要创建一个帮助类来表示角色的位置. 这个类也包含两个方法来计算两个点间的距离。在这个类里的每个事
都应该能被自解释的... 即使你已经对几何生疏了,你也必须相信我正确的计算了两点间的距离。:)
{
public class Position
{
private int _x;
private int _y;
public int X
{
get { return _x; }
set { _x = value; }
}
public int Y
{
get { return _y; }
set { _y = value; }
}
public Position()
{
_x = _y = 0;
}
public Position(int x, int y)
{
_x = x;
_y = y;
}
// This could be done on 1 line, but I broke
// it apart for readability.
public double CalcDistance(Position pos)
{
int xsquared = (this.X - pos.X) * (this.X - pos.X);
int ysquared = (this.Y - pos.Y) * (this.Y - pos.Y);
int sum = xsquared + ysquared;
return Math.Sqrt(Convert.ToDouble(sum));
}
public override string ToString()
{
return _x.ToString() + “, “ + _y.ToString();
}
}
}
我们要把规则简化,我们要有两个类,射箭手类和击剑手类。我们假设一个弓箭手只能在距离
玩家四到十个单元的范围内攻击玩家。一个击剑手必须在两个单元格内攻击玩家。我们创建一个
玩家,它必须实作ISubject 接口,因为它要做为 subject . 玩家也要有一个属性表明我们当前
的位置.
{
public class Player : ISubject
{
private List<IObserver> _observers;
private Position _currentPosition;
public Position CurrentPosition
{
get { return _currentPosition; }
set { _currentPosition = value; }
}
public List<IObserver> Observers
{
get { return _observers; }
}
// We initialize our observer list in the constructor.
public Player()
{
_observers = new List<IObserver>();
}
// This is the key event in our scenario. The only thing
// that the observers care about is the position of the
// player, so we notify them when it changes.
public void SetPosition(Position pos)
{
_currentPosition = pos;
Console.WriteLine(“Player is at “ + pos.ToString());
NofifyObservers();
}
// This is our function to register a new observer.
public void RegisterObserver(IObserver observer)
{
if (!_observers.Contains(observer))
_observers.Add(observer);
}
// This takes an observer out of the list.
public void UnregisterObserver(IObserver observer)
{
if (_observers.Contains(observer))
_observers.Remove(observer);
}
// This does the notification. In our case
// that means each observer will try to attack
// based on the position of the player.
public void NofifyObservers()
{
foreach (IObserver obs in _observers)
obs.Attack(ref _currentPosition);
}
}
}
我们已经建立subject . 现在我们需要实现几个观察者,注意到,RegisterObserver 方法和
UnregisterObserver 方法 只是简单的从有效的观察者内部列表里添加和删除观察者。这些
方法都传递一个IObserver 参数,这意味着它们会接受任何实现IObserver 接口的对象。NotifyObserver
方法简单的轮循观察者列表,并且调用每一个对象的Attack 方法。我们知道每一个观察者都有
这样的方法,因为在IObserver 接口内被指定了。
之前提到过,我们实现了两个观察者为这个实例,弓箭手类和击剑手类,下面是它们的实现:
{
public class Archer : IObserver
{
private string _name;
private Position _current;
public string Name
{
get { return _name; }
set { _name = value; }
}
public Position Current
{
get { return _current; }
set { _current = value; }
}
public Archer(string name)
{
_name = name;
_current = new Position(0, 0);
}
public void SetPosition(Position pos)
{
_current = pos;
}
public void Attack(ref Position p)
{
// Archer can attack if use is between 4 and 10
// units away.
double distance = p.CalcDistance(_current);
string formatted = String.Format(“{0:0.00}”, distance);
if (distance >= 4 && distance <= 10)
Console.WriteLine(_name
+ ” is attacking! [” + formatted + “]”);