目录:设计模式之小试牛刀
源码路径:Github-Design Pattern
定义:(Observer Pattern)
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
类图:
启示:
最近工作好不繁忙,累的是腰酸脖子疼,这不周末了决定忙里偷闲,去公园散散步,换换心情。
逃离了键盘鼠标,整个世界都清净了。
漫步在公园小径,吹着小风晒着太阳补着钙,好不自在。
走到池塘边,一阵铃铛声吸引了我的注意力。定睛一看,原来是鱼儿咬钩了。只见垂钓者赶紧从旁边的草坪上跑过来收杆拉线,好家伙,钓了个够猫尝个腥塞个牙缝的鱼儿。取下鱼,重新放上鱼饵,把钩一摔,垂钓者就撒手去旁边草坪和朋友打牌去了。和旁边其他一心垂钓的钓客相比,这货明显是更会享受啊,即钓了鱼又晒了太阳打了牌,简直开了多线程啊。
我这人就爱灵光一闪,这不就是观察者模式吗?!
鱼竿,铃铛,垂钓者。
鱼竿是被观察者,
铃铛是通知工具,
垂钓者是观察者。
鱼儿咬钩,鱼竿通过铃铛通知垂钓者收钩。
多么经典的观察者模式生活实例啊。
这灵光一闪,可是要立马记录啊。打开手机滴答清单记下【应用观察者模式实现钓鱼示例】。
回到家,打开电脑,一会噼里啪啦,就完成了以下观察者模式示例。
代码:(简单实现)
先来定义鱼的品类枚举:
public enum FishType
{
鲫鱼,
鲤鱼,
黑鱼,
青鱼,
草鱼,
鲈鱼
}
接下来申明一个钓鱼工具的抽象类,维护订阅者列表,并负责循环通知订阅者。
///
/// 钓鱼工具抽象类
/// 用来维护订阅者列表,并通知订阅者
///
public abstract class FishingTool
{
private readonly List _subscribers;
protected FishingTool()
{
_subscribers = new List();
}
public void AddSubscriber(ISubscriber subscriber)
{
if (!_subscribers.Contains(subscriber))
_subscribers.Add(subscriber);
}
public void RemoveSubscriber(ISubscriber subscriber)
{
if (_subscribers.Contains(subscriber))
_subscribers.Remove(subscriber);
}
public void Notify(FishType type)
{
foreach (var subscriber in _subscribers)
subscriber.Update(type);
}
}
鱼竿的实现,这里用随机数模拟鱼儿咬钩:
///
/// 鱼竿
///
public class FishingRod : FishingTool
{
public void Fishing()
{
Console.WriteLine("开始下钩!");
//用随机数模拟鱼咬钩,若随机数为偶数,则为鱼咬钩
if (new Random().Next() % 2 == 0)
{
var type = (FishType) new Random().Next(0, 5);
Console.WriteLine("铃铛:叮叮叮,鱼儿咬钩了");
Notify(type);
}
}
}
定义简单的观察者接口:
///
/// 订阅者(观察者)接口
/// 由具体的订阅者实现Update()方法
///
public interface ISubscriber
{
void Update(FishType type);
}
垂钓者实现观察者接口,并定义了Name,FishCount属性:
///
/// 垂钓者实现观察者接口
///
public class FishingMan : ISubscriber
{
public FishingMan(string name)
{
Name = name;
}
public string Name { get; set; }
public int FishCount { get; set; }
public void Update(FishType type)
{
FishCount++;
Console.WriteLine("{0}:钓到一条[{2}],已经钓到{1}条鱼了!", Name, FishCount, type);
}
}
来看看场景类:
///
/// 测试简单实现的观察者模式
///
private static void SimpleObserverTest()
{
Console.WriteLine("简单实现的观察者模式:");
Console.WriteLine("=======================");
//1、初始化鱼竿
var fishingRod = new FishingRod();
//2、声明垂钓者
var jeff = new FishingMan("圣杰");
//3、将垂钓者观察鱼竿
fishingRod.AddSubscriber(jeff);
//4、循环钓鱼
while (jeff.FishCount < 5)
{
fishingRod.Fishing();
Console.WriteLine("-------------------");
//睡眠5s
Thread.Sleep(5000);
}
}
以上就是观察者模式的简单实现,通过此例,是不是发现观察者模式也不过如此。
代码:(委托实现)
.Net中有一个好东西,就是委托,对它不了解的可以看看这篇博文C# 中的委托和事件。
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
那委托在观察者模式中如何应用呢,让我们来以且听我娓娓道来:
有了委托,我们就不再需要定义专门的抽象被观察者对象了,直接实现鱼竿:
///
/// 鱼竿
///
public class FishingRod
{
public delegate void FishingHandler(FishType type); //声明委托
public event FishingHandler FishingEvent; //声明事件
public void Fishing()
{
Console.WriteLine("开始下钩!");
//用随机数模拟鱼咬钩,若随机数为偶数,则为鱼咬钩
if (new Random().Next() % 2 == 0)
{
var a = new Random(10).Next();
var type = (FishType) new Random().Next(0, 5);
Console.WriteLine("铃铛:叮叮叮,鱼儿咬钩了");
if (FishingEvent != null)
FishingEvent(type);
}
}
}
因为被观察者定义了委托,我们也没必要定义专门的观察者接口,只需要在具体的观察者中实现对应的委托即可。
///
/// 垂钓者(观察者)
///
public class FishingMan
{
public FishingMan(string name)
{
Name = name;
}
public string Name { get; set; }
public int FishCount { get; set; }
public void Update(FishType type)
{
FishCount++;
Console.WriteLine("{0}:钓到一条[{2}],已经钓到{1}条鱼了!", Name, FishCount, type);
}
}
看看场景类:
///
/// 测试委托实现的观察者模式
///
private static void DelegateObserverTest()
{
Console.WriteLine("委托实现的观察者模式:");
Console.WriteLine("=======================");
//1、初始化鱼竿
var fishingRod = new DelegateImplement.FishingRod();
//2、声明垂钓者
var jeff = new DelegateImplement.FishingMan("圣杰");
//3、注册观察者
fishingRod.FishingEvent += jeff.Update;
//4、循环钓鱼
while (jeff.FishCount < 5)
{
fishingRod.Fishing();
Console.WriteLine("-------------------");
//睡眠5s
Thread.Sleep(5000);
}
}
总结:
观察者模式中有两个关键字,通知和更新。
被观察者状态改变通知观察者做出相应更新。
解决的是当对象改变时需要通知其他对象做出相应改变的问题。
优缺点:
优点
观察者和被观察者之间是抽象耦合,易于扩展;
可构造一套触发机制,形成广播链,支持广播通信;
缺点
若一个对象存在较多的观察者,则通知观察者存在效率问题;
观察者和被观察者间可能会导致循环依赖,导致系统奔溃。
应用场景:
- 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
- 事件多级触发场景。
- 跨系统的消息交换场景,如消息队列的处理机制。