设计模式系列-观察者模式
天气渐渐开始变暖和了,天天做办公室写代码感觉身体越来越差了,该活动活动了,跟快乐技术沙龙嗨皮吧秦春林交流后决定天气暖和了在群里发布一次小的户外活动,大家一起出去爬爬山或者运动运动,放松放松自己的身体和心情啊!嗨皮吧QQ群:190784175!
一、案例场景
没错,按惯例上面就是今天设计模式的场景,那么我们来分析一下下面的场景吧:首先,我们此次户外活动的组织者是快乐技术沙龙嗨皮吧(命名为:HappyBar)。嗨皮吧负责维护所有参与的成员,并且通知成员活动的具体时间与集合的地点,并且成员需要观察嗨皮吧的通知来获得活动的消息。那么我们来用代码实现一把:
1.快乐技术沙龙HappyBar的实现:
① 首先需要维护一个成员的集合,这个集合存放所有参加活动的成员,包括添加成员,如果有成员临时有事就需要从集合中移除,所以还需要一个移除成员。
② 接下来有一个发布通知的方法通知所有成员活动信息已经发布了。
③ 最后就是活动的详细信息、代码如下:
/// 嗨皮吧 春游计划
/// </summary>
public class HappyBar
{
//维护QQ群报名参与活动的成员
List<QQMember> memberList = new List<QQMember>();
/// <summary>
/// 增加参与活动的成员
/// </summary>
/// <param name="member">参与活动的成员</param>
public void Attach(QQMember member)
{
memberList.Add(member);
}
/// <summary>
/// 移除活动的成员
/// </summary>
/// <param name="member"></param>
public void Detach(QQMember member)
{
memberList.Remove(member);
}
/// <summary>
/// 发布活动的通知
/// </summary>
public void Notify()
{
memberList.ForEach(p => p.GoDestination());
}
/// <summary>
/// 活动发布的信息
/// </summary>
public string PublishInfo
{
get;
set;
}
}
2. 活动参与者成员Member的实现
①参与人有自己的基本信息例如名字。
②参与人要读取活动的通知并且按时前往目的地,代码如下:
/// 嗨皮吧QQ群成员类
/// </summary>
public class QQMember
{
//参与人姓名
public string Name { get; set; }
//该参与人需要关注的通知来源
public HappyBar happyBar { get; set; }
/// <summary>
/// 实际行动,根据活动信息前往目的地
/// </summary>
public void GoDestination()
{
Console.WriteLine("{0} 正在前往 {1}..",Name,happyBar.PublishInfo);
}
}
3.主函数调用代码如下:
{
//春游活动组织发起者
HappyBar bar = new HappyBar();
QQMember memberZhang = new QQMember() { Name="张智", happyBar = bar };
QQMember memberDing = new QQMember() { Name="丁朝阳", happyBar = bar };
QQMember memberZheng = new QQMember() { Name = "郑亚敏", happyBar = bar };
bar.Attach(memberZhang); //张智参加活动
bar.Attach(memberDing); //丁朝阳参加活动
bar.Attach(memberZheng); //郑亚敏参加互动
bar.Detach(memberZhang); //张智临时有事,取消活动
//设置活动信息
bar.PublishInfo = " 怀柔水长城 时间:2012年4月XX日";
//发起活动及通知
bar.Notify();
}
运行结果如下:
二、观察者模式
上边的代码显然耦合度太高,如果这个时候我流程发起的组织不是嗨皮吧了呢? 是不是要重新加一个发起的类,并且修改成员关注通知的属性。在如果发起的活动不是指面对QQ群成员,还面对社区成员呢?是不是就需要加入一个社区成员的成员类,那么活动发起类的集合就需要重构了。观察者模式提供了一个很好的方案,即能扩展又能满足我们现有的需求。
1. 观察者模式介绍
观察者模式类图如下:
① Subject接口:抽象了主题类,这样将来不管是什么主题只要实现这个接口就可以发起主题活动。
② Observer接口:抽象了观察者类,不管是什么类型的观察者都可以通过实现观察者接口来具有观察主题通知的功能。
③ ConcreteSubject类:实现了主题抽象,实现具体的功能。对应我们上边的 HappyBar类,用来维护和发起活动通知。
④ ConreteObserver类:实现了观察者的抽象,实现具体功能。对应我们上边的QQMember类,用来观察主题类的通知,并更新自身的状态。
2.重构我们的代码
首先我们需要抽象出主题类的规则与观察者类,这样我们将来不管是什么主题和观察者,都可以通过实现各自定义抽象来扩展程序,从而避免了修改程序。代码如下:
{
//添加成员
void Attach(IObserver member);
//移除成员
void Detach(IObserver member);
//通知
void Notify();
}
public interface IObserver
{
//根据通知更新自己
void GoUpdate();
}
接下来我们需要将我们之前的主题类也就是活动发起类HappyBar做一些修改让自己实现ISubject接口,并修改成员集合的类型为抽象的IObserver接口类型,代码如下:
/// 嗨皮吧 春游计划
/// </summary>
public class HappyBar : ISubject
{
//维护QQ群报名参与活动的成员
List<IObserver> memberList = new List<IObserver>();
/// <summary>
/// 增加参与活动的成员
/// </summary>
/// <param name="member">参与活动的成员</param>
public void Attach(IObserver member)
{
memberList.Add(member);
}
/// <summary>
/// 移除活动的成员
/// </summary>
/// <param name="member"></param>
public void Detach(IObserver member)
{
memberList.Remove(member);
}
/// <summary>
/// 发布活动的通知
/// </summary>
public void Notify()
{
memberList.ForEach(p => p.GoUpdate());
}
/// <summary>
/// 活动发布的信息
/// </summary>
public string PublishInfo
{
get;
set;
}
}
继续重构成员类的实现,一样实现成员类的接口即可。代码如下:
/// 嗨皮吧QQ群成员类
/// </summary>
public class QQMember : IObserver
{
//参与人姓名
public string Name { get; set; }
//该参与人需要关注的通知来源
public HappyBar happyBar { get; set; }
/// <summary>
/// 实际行动,根据活动信息前往目的地
/// </summary>
public void GoUpdate()
{
Console.WriteLine("{0} 正在前往 {1}..",Name,happyBar.PublishInfo);
}
}
主函数调用如下:
HappyBar bar = new HappyBar();
IObserver memberZhang = new QQMember() { Name="张智", happyBar = bar };
IObserver memberDing = new QQMember() { Name = "丁朝阳", happyBar = bar };
IObserver memberZheng = new QQMember() { Name = "郑亚敏", happyBar = bar };
bar.Attach(memberZhang); //张智参加活动
bar.Attach(memberDing); //丁朝阳参加活动
bar.Attach(memberZheng); //郑亚敏参加互动
bar.Detach(memberZhang); //张智临时有事,取消活动
//设置活动信息
bar.PublishInfo = " 怀柔水长城 时间:2012年4月XX日";
//发起活动及通知
bar.Notify();
3.程序的扩展
这时我们如果想要新增一个主题发起者不是嗨皮吧,而是参加Windows Phone7 与 Windows8开发者训练营的主题,发起者是微软。我们扩展的代码如下:
{
List<IObserver> memberList = new List<IObserver>();
/// <summary>
/// 增加参与活动的成员
/// </summary>
/// <param name="member">参与活动的成员</param>
public void Attach(IObserver member)
{
memberList.Add(member);
}
/// <summary>
/// 移除活动的成员
/// </summary>
/// <param name="member"></param>
public void Detach(IObserver member)
{
memberList.Remove(member);
}
/// <summary>
/// 发布活动的通知
/// </summary>
public void Notify()
{
memberList.ForEach(p => p.GoUpdate());
}
/// <summary>
/// 活动发布的信息
/// </summary>
public string PublishInfo
{
get;
set;
}
}
当然可以参与的成员有,开发者、学生、老板等等类型的观察者,我们只要将不同类型的观察者类型实现对应的观察者抽象即可,代码如下:
/// 开发者
/// </summary>
public class Developer : IObserver
{
//参与人姓名
public string Name { get; set; }
//该参与人需要关注的通知来源
public ISubject subject { get; set; }
/// <summary>
/// 实际行动,根据活动信息前往目的地
/// </summary>
public void GoUpdate()
{
Console.WriteLine("{0} 正在前往 {1}..", Name, subject.PublishInfo);
}
}
/// <summary>
/// 学生
/// </summary>
public class Student : IObserver
{
//参与人姓名
public string Name { get; set; }
//该参与人需要关注的通知来源
public ISubject subject { get; set; }
/// <summary>
/// 实际行动,根据活动信息前往目的地
/// </summary>
public void GoUpdate()
{
Console.WriteLine("{0} 正在前往 {1}..", Name, subject.PublishInfo);
}
}
主函数调用如下:
{
//微软开发者训练营
MicrosoftCenter mc = new MicrosoftCenter();
IObserver member1 = new Developer() { Name = "张三", happyBar = mc };
IObserver member2 = new Developer() { Name = "李四", happyBar = mc };
mc.Attach(member1);
mc.Attach(member2);
mc.PublishInfo = "微软望京开发者训练营";
mc.Notify();
}
运行结果如下:
三、C#版观察者模式
理解了观察者模式这种通知机制后,我们能感觉到,就是当发起通知时去调用通知所有成员。这类似于C#中的委托,如果该场景在C#中应用,使用委托可以更灵活方便,代码不一定非要模式,简单就是美!使用委托重构方法如下:
1.声明一个通知更新的委托:
delegate void EventHandler();
2.定义春游活动主题:
/// 嗨皮吧 春游计划
/// </summary>
public class HappyBar
{
//定义一个事件,当发布活动的时候触发
public event EventHandler GoUpdate;
/// <summary>
/// 发布活动的通知
/// </summary>
public void Notify()
{
GoUpdate();
}
/// <summary>
/// 活动发布的信息
/// </summary>
public string PublishInfo
{
get;
set;
}
}
3.定义参与者(观察者)成员
/// 嗨皮吧QQ群成员类
/// </summary>
public class QQMember
{
//参与人姓名
public string Name { get; set; }
//该参与人需要关注的通知来源
public HappyBar happyBar { get; set; }
/// <summary>
/// 实际行动,根据活动信息前往目的地
/// </summary>
public void GoUpdate()
{
Console.WriteLine("{0} 正在前往 {1}..", Name, happyBar.PublishInfo);
}
}
4.主函数调用如下:
{
HappyBar hb = new HappyBar();
QQMember member1 = new QQMember() { Name = "张三", happyBar = hb };
QQMember member2 = new QQMember() { Name = "李四", happyBar = hb };
hb.GoUpdate += new 观察者模式1.EventHandler(member1.GoToUpdate);
hb.GoUpdate += new 观察者模式1.EventHandler(member2.GoToUpdate);
hb.Notify();
}
5.运行结果如下:
观察者模式结束啦,户外活动具体时间还没有确定,使用委托事件的代码现在还是处于高耦合的状态,如何解耦就看大家啦,可以把代码以留言的形式跟大家分享!~ 呵呵,顺便给个推荐吧!写的很辛苦啊!