这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

1.0版本(双向耦合)

我们描述一个工作做的实际场景来说一说观察者模式,我们在公司的时候会加入很多的群,微信群、QQ群、企业微信群、钉钉群等,他们都有一些公共的功能,第一个功能是可以邀请员工加入,第二个功能是发布群公告来通知群成员。我们员工和工作群之间就形成了符合观察者模式的特点,多位员工关注(订阅)着群公告,工作群在有重要事件的时候进行信息的发布。
image.png

WeCharGroup类

  1. class WeCharGroup {
  2. members: Programmer[] = []; // 群里面的程序员列表
  3. notice: string; // 公告
  4. addMember(member: Programmer): void {
  5. this.members.push(member);
  6. }
  7. setNotice(notice): void {
  8. this.notice = notice;
  9. }
  10. getNotice(): string {
  11. return this.notice;
  12. }
  13. notifyAll(): void {
  14. this.members.forEach(v => v.update());
  15. }
  16. }

Programmer类

  1. class Programmer {
  2. name: string; //程序员是谁?
  3. swg: WeCharGroup; //程序员在哪个群?
  4. constructor(name: string, swg: WeCharGroup) {
  5. this.name = name;
  6. this.swg = swg;
  7. }
  8. //程序员接收新消息。
  9. update() {
  10. console.log(
  11. `${this.name}您有一条新的群公告,请注意接收!\n${this.swg.getNotice()}`
  12. );
  13. }
  14. }

我们通过面向对象的编程思想构造出了WeCharGroup、Programmer两个对象,它们的功能和属性在上面的UML类图和代码中都明确的体现了,我们现在就一起来体验一下吧!

体验进行中

  1. 第一步:创建我们的工作群。
  2. 第二步:将公司的前端小张和后端小王邀请入群并让他俩及时获取最新的群公告信息。
  3. 第三步:今天就是每周的最后一天来,设置一个公告(“下班前记得把周报写完再走。”)。
  4. 第四步:发布最新公告。

上面的步骤操作完以后,我们的小张和小王就收到了记得写周报的通知,HXD们周报写完了吗?

  1. // 第一步:公司来个人把工作群先建起来
  2. const workGroup = new WeCharGroup();
  3. // 通过addMember将程序员加入群组,程序员通过群组获得最新公告
  4. workGroup.addMember(new Programmer('前端小张', workGroup));
  5. workGroup.addMember(new Programmer('后端小王', workGroup));
  6. // 设置群公告
  7. workGroup.setNotice('下班前记得把周报写完再走。');
  8. // 发布公告
  9. workGroup.notifyAll();

2.0版本(第一次解耦)

通过看1.0版本的UML类图可以看的出来,我们的微信群看似只能邀请程序员,这怎么能行呢,测试工程师也需要看到公告呀,还有那谁也得看公告的呀。好的我们来针对这个现象进行一次改造吧,我们的需求是什么?多种员工都能在工作群发布公告后通过update来获取到最新的公告,我们需要将现有的Programmer类进行抽象封装,因为我们的员工都要对工作群进行关注,所有我们的抽象类定义为Observer。
image.png

Observer抽象类

  1. abstract class Observer {
  2. name: string; // 说要关注群公告?
  3. swg: WeCharGroup; //关注的哪个群公告?
  4. abstract update(): void; // 接收新消息。
  5. }

Programmer类2.0

  1. class Programmer extends Observer {
  2. constructor(name: string, swg: WeCharGroup) {
  3. super();
  4. this.name = name;
  5. this.swg = swg;
  6. }
  7. //程序员接收新消息。
  8. update() {
  9. console.log(
  10. `${this.name}您有一条新的群公告,请注意接收!\n${this.swg.getNotice()}`
  11. );
  12. }
  13. }

WeCharGroup类2.0

  1. class WeCharGroup {
  2. members: Observer[] = []; // 群里面的程序员列表
  3. ...
  4. addMember(member: Observer): void {
  5. this.members.push(member);
  6. }
  7. ...
  8. }

TestEngineer类

  1. class TestEngineer extends Observer {
  2. constructor(name: string, swg: WeCharGroup) {
  3. super();
  4. this.name = name;
  5. this.swg = swg;
  6. }
  7. //测试工程师接收新消息。
  8. update() {
  9. console.log(
  10. `${this.name}您有一条新的群公告,请注意接收!\n${this.swg.getNotice()}`
  11. );
  12. }
  13. }

体验一下

  1. 我们在版本1.0的第二步将测试小李邀请入群吧

在我们将观察者抽象出来后不管是我们的测试小李,还是其他岗位的同事我们都可以用最小的改动来满足他们关注群公告的需求了。

  1. // 第一步:公司来个人把工作群先建起来
  2. const workGroup = new WeCharGroup();
  3. // 通过addMember将程序员加入群组,程序员通过群组获得最新公告
  4. workGroup.addMember(new Programmer('前端小张', workGroup));
  5. workGroup.addMember(new Programmer('后端小王', workGroup));
  6. // ++ 邀请测试小李入群
  7. workGroup.addMember(new TestEngineer('测试小李', workGroup));
  8. // 设置群公告
  9. workGroup.setNotice('下班前记得把周报写完再走。');
  10. // 发布公告
  11. workGroup.notifyAll();

3.0版本(第二次解耦)

在2.0的时候我们支持了不同的员工可以关注工作群里面的公告,但是在实际的职场中我们除了关注公司的微信群很可能关注这公司的企业微信群对不对,或者还有DingDing群,好多的群让人眼花缭乱的,有多少HXD的群组比好友还多的🤔️?再看一下UML类图,我们现在强行的和微信群进行耦合,我们再来改造一把,免得用到的时候手足无措。我们将群组的功能进行抽象命名为Subject。
image.png
Subject

  1. interface Subject {
  2. addMember(member: Observer): void;
  3. setNotice(notice: string): void;
  4. getNotice(): string;
  5. notifyAll(): void;
  6. }

DingDingGroup

  1. class DingDingGroup {
  2. members: Observer[] = []; // 群里面的员工列表
  3. notice: string; // 公告
  4. addMember(member: Observer): void {
  5. this.members.push(member);
  6. }
  7. setNotice(notice): void {
  8. this.notice = notice;
  9. }
  10. getNotice(): string {
  11. return this.notice;
  12. }
  13. notifyAll(): void {
  14. this.members.forEach(v => v.update());
  15. }
  16. }

WeCharGroup3.0

  1. class WeCharGroup implements Subject {
  2. ...
  3. }

Observer2.0

  1. abstract class Observer {
  2. ...
  3. swg: Subject; //关注的哪个群公告?
  4. ...
  5. }

Programmer3.0&TestEngineer3.0

  1. class Programmer extends Observer {
  2. constructor(name: string, swg: Subject) {
  3. super();
  4. this.name = name;
  5. this.swg = swg;
  6. }
  7. ...
  8. }
  9. class TestEngineer extends Observer {
  10. constructor(name: string, swg: Subject) {
  11. super();
  12. this.name = name;
  13. this.swg = swg;
  14. }
  15. ...
  16. }

最后我们在体验一把

  1. 第一步:创建一个钉钉群
  2. 第二步:邀请三位同学加入日常群
  3. 第三步:设置群公告
  4. 第四步:发布公告 ```typescript // 公司新建了一个日常钉钉群 const everyday = new DingDingGroup(); everyday.addMember(new Programmer(‘前端小张’, everyday)); everyday.addMember(new Programmer(‘后端小王’, everyday)); everyday.addMember(new Programmer(‘测试小李’, everyday));

// 设置群公告 everyday.setNotice(‘各位同事周末愉快!!!’); // 发布公告 everyday.notifyAll(); ```

总结

  1. 观察者模式也称为发布-订阅模式。
  2. 观察者模式定义一对多的依赖关系,让多个观察者同时关注一个主题对象,并在主题对象发生变化后通知观察者尽心更新。

注:最近翻了翻好早前买的《大话设计模式》,感觉还有很有意思呀。😯