观察者模式
该模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能自动更新自己。
//Subject类,可翻译为主题或者抽象通知类,一般用一个抽象类或一个接口实现。
//它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象
abstractclass Subject
{
private IList<Observer> observers = new IList<Observer>();
public void Attach(Observer o)
{
observers.Add(observer);
}
public void Detach(Observer o)
{
observers.Remove(o);
}
public void Notify()
{
for each (Observer var in observers)
{
o.Update();
}
}
};
//抽象观察者,为所有的具体观察者提供一个接口。在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或接口实现。
//更新接口通常包含一个Update()方法,即更新方法
abstractclass Observer
{
public abstract void Update();
};
//具体主题或者说具体被观察者,具体通知者,这里将有关状态存入观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知。
class ConcretSubject :Subject
{
private string subjectState;
//具体被观察者的状态
public string SubjectState
{
get{ return subjectState; }
set{ subjectState = value; }
}
};
//具体观察者
class ConcretObserver
{
private string name;
private string observerState;
private ConcretSubject subject;
public ConcretObserver(ConcretSubject sub, string name)
{
subject = sub;
this.name = name;
}
public override void Update()
{
observerState = subject.subjectState;
Console.WriteLine("观察者{0}的新状态是:{1}", name, observerState);
}
public ConcretSubject Subject
{
get{ return subject; }
set{ subject = value; }
}
};
//客户端代码
void main()
{
ConcretSubject s = new ConcretSubject();
s.Attach(new ConcretObserver(s,"X"));
s.Attach(new ConcretObserver(s,"Y"));
s.Attach(new ConcretObserver(s,"Z"));
s.subjectState = "ABC";
s.Notufy();
}
//得到的结果为
观察者X的新状是ABC
观察者Y的新状是ABC
观察者Z的新状是ABC
下面举一个例子,比如在一个公司里,有员工A、B两个人,A在看股票,B在看NBA,然而有两个通知者,一个是前台,如果老板回来了就会通知这两位立刻回去工作;一个就直接是老板,在前台来不及通知的情况下,A和B被老板发现,相当于被通知。
interface Subject
{
void Attach(Observer o);
void Detach(Observer o);
void Notify();
string SubjectState
{
get;
set;
}
};
class Boss :Subject
{
private IList<Observer> observers = newIList<Observer>();
private string action;
public void Attach(Observer o)
{
observers.Add();
}
public void Detach(Observer o)
{
observers.Remove();
}
public void Notify()
{
for each (Observer var in observers)
{
var.Update();
}
}
public string SubjectState
{
get{ return action; }
set{ action = value; }
}
};
class Head :Subject
{
private IList<Observer> observers = newIList<Observer>();
private string action;
public void Attach(Observer o)
{
observers.Add();
}
public void Detach(Observer o)
{
observers.Remove();
}
public void Notify()
{
for each (Observer var in observers)
{
var.Update();
}
}
public string SubjectState
{
get{ return action; }
set{ action = value; }
}
};
//抽象观察者
abstractclass Observer
{
protected string name;
protected Subject sub;
public Observer(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
public abstract void Upadte();
};
//看股票的同事
class StockObserver :Observer
{
public StockObserver(string name, Subject sub) :base(name, sub)
{}
public override void Update()
{
Console.WriteLine("{0} {1} 关闭股票继续工作", sub.SubjectState, name);
}
};
//看NBA的同事
class NBAObserver :Observer
{
public NBAObserver(string name, Subject sub) :base(name, sub)
{}
public override void Update()
{
Console.WriteLine("{0} {1} 关闭NBA继续工作",sub.SubjectState,name);
}
};
void main()
{
Boss X = new Boss();
StockObserver t1 = new StockObserver("A", X);
NBAObserver t2 = new NBAObserver("B", X);
X.Attach(t1);
X.Attach(t2);
X.SubjectState = "Boss 回来了";
X.Notify();
}
可以看出来,两个类相互耦合,前台或者老板需要通知员工,然后员工也需要知道前台或老板的状态。但是我们这里将类抽象化,使得各个具体类之间不再耦合,而是依赖抽象。
我们为什么要用观察者模式呢?
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各个类紧密耦合,这样会给维护、扩展带来不便
而我们这里利用了抽象化,Subject发出通知时并不需要知道谁是它的观察者,任何一个具体的观察者不知道也不需要知道其他观察者的存在
什么时候使用观察者模式呢?
当一个对象的改变需要同时改变其他对象的时候,且其不知道具体有多少给对象需要改变的时候,应该考虑使用观察者模式
当一个抽象模型由两个方面,其中一方面依赖于另一方面,这时候用观察者模式可以将这两者封装在独立的对象中使他们各自独自的改变和复用
观察者模式所用做的工作就是在解除耦合,让耦合的双方都依赖于抽象而不是依赖于具体。从而使各自的变化都不会影响对方的变化
观察者模式的不足
书中提出了关于visual studio中可能使用观察者模式的例子,比如在点击运行后,整个界面都会发生变化。且每个窗体都是事先封装好的,也就没有了抽象观察者这个接口了,按我们的想法通知的功能就完成不了了。
另外就是每个具体观察者,其notify的方法不一定一样,有的窗体是要打开,而有的窗体则是要隐藏。于是引出了下面的方法。
事件委托实现
现在我们只有“看股票的员工”和“看NBA的员工”的具体类,没有了父类。因为现实中往往就是如此,方法名不同,且只能得到具体类。
//看股票的员工
class StockObserver
{
private string name;
private Subject sub;
public StockObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
public void CloseStocktoWork() //提醒方法
{
Console.WriteLine("{0}{1} 关闭股票回去工作", sub.SubjectState, name);
}
};
//看NBA的员工
class NBAObserver
{
private string name;
private Subject sub;
public NBAObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
public void CloseNBAtoWork() //提醒方法
{
Console.WriteLine("{0}{1} 关闭NBA回去工作", sub.SubjectState, name);
}
};
//通知者接口
interface Subject
{
void Notify();
string SubjectState
{
get;
set;
}
};
//声明一个委托,名称叫EventHandler,无参数,无返回值
delegatevoid EventHandler();
//Boss类
class Boss :Subject
{
public event EventHandler Update; //声明一“EventHandler”的委托事件,名称叫“Update”
private string action;
public void Notify()
{
Update(); //在访问通知方法时,调用更新
}
public string SubjectState
{
get{ return action; }
set{ action = value; }
}
};
//前台类
class Head :Subject
{
//与Boss类类似
};
//客户端
void main()
{
Boss X = new Boss();
StockObserver A = new StockObserver("A", X);
NBAObserver B = new NBAObserver("B", X);
//将看股员工的关闭股票的方法和 看NBA的关闭NBA的方法挂钩到老板的Update上,也就是将两不同类的不同方法委托给老板类的Update了。
X.Update += newEventHandler(A.CloseStocktoWork);
X.Update += new EventHandler(B.CloseNBAtoWork);
X.SubjectState = "Boss iscoming";
X.Notify();
}
事件委托说明
委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,委托可看作是对函数的抽象,是函数的“类
也就是说:
delegate void EventHandler(); 可以理解为声明了一个特殊的类;
public event EventHandler Update; 即声明了一个类的变量(就像普通类声明对象但不分配内存那种)
new EventHandler(A.CloseStocktoWork);就是一个委托的实例
一个委托可搭载多个方法,所有方法被依次唤起。可以使委托对象所搭载的方法并不需要属于同一个类。
但是,委托对象所搭载的方法必须具有相同的原型和形式,相同的参数列表和返回值类型”