生活中,当某件事发生时,应该通知所有的相关者。例如,上课地点有变,要通知所有学习这门课的同学。
在软件设计中,当一个对象的状态发生变化是,如何通知与它相关的所有对象,就是我们今天要谈到的观察者模式。
观察者模式
概述
定义了一种一对多的依赖关系。让多个观察者对象同事监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有的观察者对象,使它们能够自动更新自己。
实际上,观察者模式所做的工作就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
结构图
示例
例如《大话设计模式》中谈到的前台得知看到老板回来,把消息发给 看股票行情的员工,员工更新自己的状态。
弊端:首先,只有前台发消息。员工对前台的依赖太强。因为员工需要前台的状态。如果正好前台不在,那么员工就不能得到最新消息。其次,前台对员工的依赖太强。
如果又有新的员工看电视剧,那么前台就要更改。解耦实践一:增加抽象观察者抽象类。让两个观察者去继承”抽象观察者”,对于 Update的方法做重写操作。前台类就可以把与‘具体观察者’耦合的地方改成“抽象观察者”。
解耦实践二:增加抽象通知者接口)。让通知者去实现“抽象通知者”,这个接口。具体观察者类就可以把与‘前台’耦合的地方改成针对抽象通知者。
注: 解耦的过程中,增加抽象类或抽象接口都是可以的。他们的区别在于,抽象类可以共用一些代码,而用接口只是方法上的实现。
来看代码:
class Program { //客户端 static void Main(string[] args) { Boss huhansan = new Boss(); //看股票的同事 StockObserver tongshi1=new StockObserver("委管",huhansan ); huhansan.Attach(tongshi1); huhansan.Attach(tongshi2); huhansan.Detach(tongshi1); //老板回来 huhansan.SubjectState = "我胡汉三回来了"; huhansan.Notify(); } //通知者接口 interface Subject { void Attach(Observer observer); void Detach(Observer observer); void Notify(); string SubjectState { get; set; } } //具体的通知者 class Boss : Subject { //要通知的同事 private IList<Observer> observers = new List<Observer>(); private string action; //增加 public void Attach( Observer observer) { observers.Add(observer); } //减少 public void Detach(Observer observer) { observers.Remove(observer); } //通知 public void Notify() { foreach (Observer o in observers) o.Update(); } //老板状态 public string SubjectState { get { return action; } set { action = value; } } } //抽象观察者 abstract class Observer { protected string name; protected Subject sub; public Observer(string name, Subject sub) { this.name = name; this.sub = sub; } public abstract void Update(); } //看股票的同事 class StockObserver : Observer { public StockObserver(string name, Subject sub) : base(name, sub) { } public override void Update() { Console.WriteLine("{0}{1}关闭股票行情,继续工作", sub.SubjectState, name); } } } }
适用情况
当一个对象的改变需要同时改变其他对象的时候,而且它不知道有多少有待改变时,应该考虑适用观察者模式。
优势
将一个系统分割成一系列相互协作的类在维护的时候,既能维持一致性,又能解除耦合,便于维护扩展和重用。
不足
一方面虽然解除了具体类之间的耦合,但是对抽象类或接口还是耦合。也就是如果没有抽像的类,相关功能就无法完成。另一方面,当得到某消息,其他相关类可能会存在不同的变化。因此调用的方法不一。
针对以上两个不足,又进行改进。
实践三
委托事件
方法是,去掉父类。声明一个委托。无参数无返回值。并在老班和前台类中 声明一事件Update,类型为委托。然后在访问通知方法时,调用‘更新’ 最后,更改客户端,将不同类的不同方法委托给‘老板’的‘更新’。
来看几个关键代码
Delegate void EventHandler(); Class Boss:Subject { Public event EventHandler Update;//声明一“EventHandler(事件处理程序)”的委托事件,名称叫“Update(更新)” Public void Notify() { Update();//在访问通知方法时调用更新 }
再有 客户端
客户端/*将“看股票者”的“关闭股票程序 ”和看NBA的关闭直播方法挂钩到“;老板更新”上,也就是将两不同类的方法委托给“老板”类的“更新”了*/ Huhansan.Update+=new EventHandLer(tongshi1.CloseStockMarket); Huhansan.Update+=new EventHandLer(tongshi2.CloseNBADirectSeeding);
委托模式
概述
委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。
适用
委托对象所搭载的所有方法必须具有相同的原型和形式,也就是拥有相同的参数列表和返回值类型。
优点
一个委托可以搭载多个方法,所有方法被依次唤起。可以使得委托对象所搭载的方法并不需要属于同一个了。
总结
观察者模式就是在解除耦合。事件委托可以实现对一个事件调用多个方法。