前言
有种发邮件,一发多的感觉,发出去的是同样的信息,只不过接收到的人不一样了。
观察者模式
英文:Observer
又名:发布-订阅(Publish/Subscribe)模式
what:
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
结构图
Subject类:
抽象通知者:实现→一般用一个抽象类或者一个接口实现,
它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
增加:attach(observers.add(observer)
移除:detach(observers.remove(observer)
Observer类:
抽象观察者,实现→一般用一个抽象类或者一个接口实现
为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。更新接口通常包含一个Update()方法,这个方法叫做更新方法。
ConcreteSubject类:
具体主题或者具体通知者,实现→具体主题角色通常用一个具体子类实现。
将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
ConcreteObserver类:
具体观察者,实现→通常用一个具体子类实现。
实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。
场景
由于晚上睡觉太晚,所以上课的时候想打个盹,这时就会告诉同桌:“嘿!老师来了叫我一声!”然后同桌非常有底气的就回了我一句:“木有问题!”
然后老师过来我这里了:“你们俩!站起来!怎么都睡着了?“
额。。。说好的叫我呢?
这个时候,观察者模式就来了,不能只依赖同桌这个具体通知者,如果有一个类来管理这个,就不会有都被逮住了,还闻不到危险已到来的气息~
应用
当一个对象的改变需要同时改变其他对象的时候。
而且它不知道具体有多少对象有待改变时
观察者模式所做的工作就是解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响到另一边的变化。
它是依赖倒转原则的最佳体现。
代码展示:
抽象通知者:
interface Subject //通知者接口
{
void Attach(Observer observer);
void Detach(Observer observer);
void Notify();
string SubjuctState
{
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 SubjuctState
{
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 TV:Observer
{
//将具体通知者boss改为抽象通知者subject
public TV(string name, Subject sub): base(name, sub)
{
}
public override void Update()
{
//将具体通知者状态改成抽象通知者状态
Console.WriteLine("{0}{1},快点关上电视,继续工作,否则扣工资!扣工资!!",sub.SubjuctState ,name);
}
}
客户端代码:
class Program
{
static void Main(string[] args)
{
//老板
Boss monkey = new Boss();
//员工
TV mm = new TV("范冰冰", monkey);
monkey.Attach(mm);
//老板回来
monkey.SubjuctState = "我回来啦!";
//发出通知
monkey.Notify();
Console.Read();
}
}
效果图:
事件委托
what
委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的”类“,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被一次唤起。它可以使得委托对象所搭载的方法并不需要属于同一个类。
委托的前提:
委托对象所搭载的所有方法必须具有相同的原型和形式,也就是拥有相同的参数列表和返回值类型。
先有观察者模式,再有的委托事件技术。
情景
”mm,我手机丢了,班里的手机号都没有了,你能把班级同学的手机号抄一份给我吗?“
”这么多人,抄起来,容易错,我给同学群发一条消息,通知他们把你的号码更新了把。“
”好的,你一定要记得发给**,##,&&哦。“
”我才不管呢,凡是在我手机里的,我都会循环遍历一遍,群发给他们的。“
”好的,你就循环遍历一下,这事就委托给你咯。“
代码展现:
委托引用了方法,看起来又简单了很多。
通知者接口:
interface Subject //通知者接口
{
void Notify();
string SubjectState
{
get;
set;
}
}
具体通知者:
//声明一个委托,名称为EventHandler(事件处理程序),无参数,无返回值
delegate void EventHandler();
class Boss:Subject
{
public event EventHandler Update; //声明委托事件,名称为Update(更新)
private string action;
public void Notify()
{
Update(); //在访问通知方法时,调用Update
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
观察者接口:
interface Observer //观察者接口
{
void Update();
}
具体观察者:
class TV //具体观察者1
{
private string name;
private Subject sub;
public TV (string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
//关闭电视
public void CloseTVtt() //更新名改为关闭
{
Console.WriteLine("{0}{1}关闭电视,抓紧工作,否则扣工资!",sub.SubjectState ,name );
}
}
//同上↑----具体观察者2
class Game
{
private string name;
private Subject sub;
public Game(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
//关闭电视
public void CloseGamett()
{
Console.WriteLine("{0}{1}关闭游戏,抓紧工作,否则扣工资!", sub.SubjectState, name);
}
}
客户端代码:
class Program
{
static void Main(string[] args)
{
//老板
Boss mongkey = new Boss();
//员工
TV mm = new TV("张三",mongkey);
Game yy = new Game ("李四",mongkey );
//将关闭方法挂钩到“老板”的更新上,将两不同类的不同方法委托给老板类的更新了。
mongkey.Update += new EventHandler(mm.CloseTVtt);
mongkey.Update += new EventHandler(yy.CloseGamett);
mongkey.SubjectState = "我回来啦!";
mongkey.Notify();
Console.Read();
}
}
效果图:
后记
搞怪一下~
观察者前期+后期委托共有10个
看了效果图后,突然感觉自己再多敲几个类,都是值得的!!!哈
学习中一定要给自己找些乐趣,不然就太枯燥了~