本帖最后由 剑弑 于 2021-3-6 20:51 编辑
首先在开始讲观察者模式之前我们先来看看现在很火自媒体软件是怎么工作的。做过自媒体的应该知道,我们要把自己精心录制的视频上传到自媒体平台,平台就会把我们的视频推送给关注我们的粉丝用户,让关注我们的粉丝用户可以第一时间享受到视频给他带来的精彩内容,而哪天粉丝用户对作者的视频不在喜欢了,取关了,平台也将不会在给些用户推送我们的视频了;当然自媒体平台不只有这功能,我们只是拿其中的一点来讲我们的观察者模式。
从上面的示例中,我们能发现分别有作者、平台、粉丝用户三种角色,其中作者中平台是1对1的关系,而且平台跟粉丝用户是1对多的关系,作者不跟粉丝有直接接触的机会(当然线下会面不是我们考虑的范畴);我们把这三种角色实现在我们的对象,毕竟万物皆对象吗,相信这难不到我们,这个实现先留给大家去思考先(可以带前问题跟下面的类图一起思考,来设计我们观察者模式)。
接下来我们先来看看观察者模式的类图:
从类图我们可以看出有两个接口,分别是Subject和UserObserver,其他的类都只是这两个接口类的实现(注:两接口间的连线在vs类图中是不应该存在的,这里只是为了更好表明关系)。
我们来看看具体的代码实现,如下:
Subject
[C#] 纯文本查看 复制代码 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
interface Subject
{
/// <summary>
/// 注册观察者
/// </summary>
/// <param name="observer"></param>
void RegisterObserver(UserObserver observer);
/// <summary>
/// 移除观察者(取消注册)
/// </summary>
/// <param name="observer"></param>
void RemoveObserver(UserObserver observer);
/// <summary>
/// 发布通知
/// </summary>
/// <param name="observer"></param>
void ReleaseObserver();
}
}
UserObserver
[C#] 纯文本查看 复制代码 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
interface UserObserver
{
void Release(string byteContent,DateTime releaseDatetime,string name);
}
}
SelfMediaSubject
[C#] 纯文本查看 复制代码 using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observer
{
class SelfMediaSubject : Subject
{
private List<UserObserver> listObserver;
#region 可整合成实体
/// <summary>
/// 视频内容
/// </summary>
private string byteContent;
/// <summary>
/// 视频发布时间
/// </summary>
private DateTime releaseDatetime;
/// <summary>
/// 视频名称
/// </summary>
private string name;
#endregion
public SelfMediaSubject()
{
listObserver = new List<UserObserver>();
}
/// <summary>
/// 注册观察者
/// </summary>
/// <param name="observer"></param>
public void RegisterObserver(UserObserver observer)
{
listObserver.Add(observer);
}
/// <summary>
/// 主题发布通知
/// </summary>
/// <param name="observer"></param>
public void ReleaseObserver()
{
foreach (var observer in listObserver)
{
observer.Release(byteContent,releaseDatetime,name);
}
}
/// <summary>
/// 移除观察者
/// </summary>
/// <param name="observer"></param>
public void RemoveObserver(UserObserver observer)
{
if (listObserver.IndexOf(observer) >= 0)
listObserver.Remove(observer);
}
/// <summary>
/// 作者发布视频
/// </summary>
public void ReleaseVideo()
{
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();
}
}
}
LiUserObserver
[C#] 纯文本查看 复制代码 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(string byteContent, DateTime releaseDatetime, string name)
{
this.byteContent = byteContent;
this.name = name;
this.releaseDatetime = releaseDatetime;
Console.WriteLine($"我是{typeof(LiUserObserver).Name}");
Console.WriteLine($"视频名称:{name},视频发布时间:{releaseDatetime}");
Console.WriteLine("======================= 华丽分割线 =================================");
}
}
}
其他几个类跟LiUserObserver大致相同,差别只是类名不同跟显示不同,有兴趣的朋友可以自行实现后面我也会把源码上传。测试代码
[C#] 纯文本查看 复制代码 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();
}
}
}
结果
通过上面的观察者模式的代码细心的朋友就会发现其实他们是可以跟上面讲解的示例的角色对应的上的;请大家认真的思考比对,把上面的类图跟示例角色做个关联吧,让他们找到自己的归属地吧!!!!============================================ 我是华丽的分割线 =============================================
============================================ 我是华丽的分割线 =============================================
============================================ 我是华丽的分割线 =============================================重要的事要说三遍!!!!!
结合上面的示例我们可以很清楚的了解到,作者对应SelfMediaSubject类,他们都是给其他对象发布东西;平台对应着Subject,他们都可以让用户关注(注册)跟取关(移除)及推送视频(发布通知);而用户们对应的是LiUserObserver类(这里是1个用户对应一个类),他们都是享受者。
到这里很多对观察者模式有所了解的朋友就会问,为什么我看到别人讲的观察者模式Subject都是抽象类而不是接口呢?是你讲错了吗?你也太不可信了。
这些朋友的疑问没错,确实是存在抽象类的观察者模式; 但我们学习要循序渐进,要先一步一步来。
观察者模式其实是存在推、拉两种类型实现的,而我们今天讲的是推,下一章我们会讲观察者模式的拉是怎么实现的;观察者模式的定义及优缺点也将在下一章来补充。
|