观察者(Observer)模式——拉
本帖最后由 剑弑 于 2021-3-6 21:47 编辑上一章我们有提到观察者模式具有推、拉两种实现,推的实现我们也在上一章讲了;哪么观察者模式拉的实现又是怎样的呢?在说之前我们先回顾下自媒体的哪个例子,作者每次上传视频平台都会第一时间通知推送给关注视频作者的粉丝用户,即使某些内容是用户不想要的也会推送,但这样的体验会很不好,我都不需要的东西你平台推送给我干嘛;这个时候用户就想了,要是能由我来确定拉取自己想要的内容就好了。
不知道大家有没有去对比下两种观察者模式的例子,他们有什么的区别!!!
推:由平台主动把视频去推送给用户,用户被动的接受;
拉:由用户主动去平台拉取视频内容;
看完示例后,让我们傲娇的来看下代码的实现(毕竟我已经知道你大概是怎样的了{:2_33:}),代码如下:
Subject
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
abstract class Subject
{
protected List<UserObserver> listObserver { get; set; } = new List<UserObserver>();
protected bool chaned { get; set; }
/// <summary>
/// 注册观察者
/// </summary>
/// <param name="observer"></param>
public void RegisterObserver(UserObserver observer)
{
listObserver.Add(observer);
}
/// <summary>
/// 主题发布通知
/// </summary>
/// <param name="arg"></param>
public void ReleaseObserver(object arg)
{
if (chaned)
{
foreach (var observer in listObserver)
{
observer.Release(this, arg);
}
}
chaned = false;
}
/// <summary>
/// 主题发布通知
/// </summary>
public void ReleaseObserver()
{
ReleaseObserver(null);
}
/// <summary>
/// 移除观察者
/// </summary>
/// <param name="observer"></param>
public void RemoveObserver(UserObserver observer)
{
if (listObserver.IndexOf(observer) >= 0)
listObserver.Remove(observer);
}
}
}
SelfMediaSubject
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
class SelfMediaSubject : Subject
{
#region 可整合成实体
/// <summary>
/// 视频内容
/// </summary>
private string byteContent;
public string ByteContent
{
get { return byteContent; }
set { byteContent = value; }
}
/// <summary>
/// 视频发布时间
/// </summary>
public DateTime releaseDatetime;
/// <summary>
/// 视频名称
/// </summary>
public string name;
#endregion
/// <summary>
/// 作者发布视频(拉)
/// </summary>
public void ReleaseVideo()
{
this.chaned = true;
ReleaseObserver();
}
/// <summary>
/// 主题数据更改
/// </summary>
/// <param name="byteContent"></param>
/// <param name="releaseDatetime"></param>
/// <param name="name"></param>
public void setVideo(string byteContent, DateTime releaseDatetime, string name)
{
this.byteContent = byteContent;
this.releaseDatetime = releaseDatetime;
this.name = name;
ReleaseVideo();
}
}
}
UserObserver
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
interface UserObserver
{
void Release(Subject aSubject, object arg = null);
}
}
LiUserObserver
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
class LiUserObserver : UserObserver
{
private Subject aSubject;
#region 可整合成实体
/// <summary>
/// 视频内容
/// </summary>
private string byteContent;
/// <summary>
/// 视频发布时间
/// </summary>
private DateTime releaseDatetime;
/// <summary>
/// 视频名称
/// </summary>
private string name;
#endregion
public LiUserObserver(Subject subject)
{
aSubject = subject;
aSubject.RegisterObserver(this);
}
public void Release(Subject aSubject, object arg = null)
{
SelfMediaSubject selfMedia = aSubject as SelfMediaSubject;
this.byteContent = selfMedia.ByteContent;
this.name = selfMedia.name;
this.releaseDatetime = selfMedia.releaseDatetime;
Console.WriteLine($"我是{typeof(LiuUserObserver).Name}");
Console.WriteLine($"视频名称:{name},视频发布时间:{releaseDatetime}");
Console.WriteLine("======================= 华丽分割线 =================================");
}
}
}
测试代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
class Program
{
static void Main(string[] args)
{
SelfMediaSubject subject = new SelfMediaSubject();
LiUserObserver liUserObserver = new LiUserObserver(subject);
LiuUserObserver liuUserObserver = new LiuUserObserver(subject);
LaiUserObserver laiUserObserver = new LaiUserObserver(subject);
subject.setVideo("1111",DateTime.Now,"生活记录1");
Console.WriteLine("移除liuUserObserver");
subject.RemoveObserver(liuUserObserver);
Console.WriteLine("****************** 华丽分割线 **********************");
subject.setVideo("2222", DateTime.Now, "生活记录2");
subject.setVideo("3333", DateTime.Now, "生活记录3");
subject.setVideo("4444", DateTime.Now, "生活记录4");
Console.ReadLine();
}
}
}
结果
类图
从上面的类图跟代码,我们可以看出Subject不在是接口,而是个抽象类;我们的注册、移除还有观察者集合都搬到了Subject,还多了个chaned的bool值(作用在此不做说明,聪明的你们一定一想就知);我们的发布通知也改成传参主题对象,我们的观察者们(LiUserObserver他们)获取数据也都变成了从主题类取值,达到了我们拉(要什么取什么)的需求。
细心的朋友会发现多了个arg的参数是干嘛用的呢?代码里面也没用到啊!
其实arg有很大的作用,比如当我们主题要传一些额外的数据时,我们就可以通过arg来传参,这样我们就不用去改动到Subject,无形中提高了我们模式的扩展性,当然你不要也行。
还有朋友会问,我能不能推、拉两种都实现呢?这样我们用起来就更方便了!不是吗?
对的!确实我们是可以推、拉一起实现,只要我们用上arg进行数据传递就成;只要给Subject增加多个设置数据方法,观察者接受收改一下设值就OK;注意别忘了设置chaned。
/// <summary>
/// 主题数据更改
/// </summary>
/// <param name="arg"></param>
public void setVideo(object arg)
{
this.chaned = true;
ReleaseObserver(arg);
}接下来我们来看看观察者模式的定义及优缺点
定义:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知并自动更新。
优点:
作者跟用户之间达到了松耦合,作者不用知道每一个用户啊什么,就可以把视频发送到用户手上。
缺点:
如果在作者(主题)跟用户(观察者)之间有循环依赖的话,作者(主题)会触发它们之间进行循环调用,导致系统崩溃。
至此我们的观察者模式就讲完了,有朋友可能会问,使用场景呢?有哪些?场景的话就留给大家去举一反三吧,毕竟说出来大家可能一会也就忘了,只有去思考了,举一反三了才能更深刻。
66666666666666 6666666 Amy尾巴 发表于 2021-3-8 08:59
6666666
6666666 ibcadmin 发表于 2021-3-7 17:25
66666666666666
666666666666666666 666666666666666666666666666666666 Empty风 发表于 2021-6-18 17:07
666666666666666666666666666666666
:L:L:L:L:L:L:L
页:
[1]