你应该要知道的设计模式

前言

整理了一下常见的设计模式

观察者模式

特点:

  1. 主题和观察者的解耦,由于不存在引用关系,使得独立地变化,互不影响。
  2. 一个主题对象可以有多个观察者对象,形成一对多的依赖关系。

image.png

简易版本主题:

// 简易版代码
class Subject{
    // 一个主题对象维护一个观察者数组
    constructor(){
        this.observerList = [];
    }
    // 添加观察者
    addObserver(observer){
        this.observerList.push(observer);
    }
    // 移除观察者
    removeObserver(observer){
        const index = this.observerList.indexOf(observer);
        this.observerList.splice(index, 1);
    }
    // 通知观察者
    notifyObserver(message){
        const observers = this.observerList;
        observers.forEach((observer) => {
            observer.notified(message);
        })
    }
}

简易版本观察者:

class Observer{
    constructor(name, subject){
        this.name = name;
        if(subject){
            subject.addObserver(this);
        }
    }
   notified(message) {
    console.log(this.name, 'got message', message);
  }
}

使用:

const subject = new Subject();
const observerA = new Observer('observerA', subject);
const observerB = new Observer('observerB');
subject.addObserver(observerB);
subject.notifyObservers('Hello from subject');
subject.removeObserver(observerA);
subject.notifyObservers('Hello again');

实现关联的两种方式:主题主动添加,观察者自行关联。

订阅发布者模式

特点:

  1. 订阅者和发布者直接不直接通信,通过订阅中心通信,实现了完全解耦
  2. 发布订阅的实现内部利用了观察者模式

image.png

简易版发布订阅中心:

/**
 * 发布订阅者模式的实现类
 */
class PubSub {
  constructor() {
    this.messages = {};   // 存储发布的消息
    this.listeners = {};  // 存储订阅的监听器
  }



  /**
   * 发布者管理
   * @param {string} type 消息类型
   * @param {*} content 消息内容
   */
  publish(type, content) {
    const existContent = this.messages[type];
    if (!existContent) {
      this.messages[type] = [];
    }

    this.messages[type].push(content);
  }

  /**
   * 订阅者管理
   * @param {string} type 消息类型
   * @param {function} cb 回调函数
   */
  subscribe(type, cb) {
    const existListener = this.listeners[type];
    if (!existListener) {
      this.listeners[type] = [];
    }
    this.listeners[type].push(cb);
  }

  /**
   * 通知订阅者
   * @param {string} type 消息类型
   */
  notify(type) {
    const messages = this.messages[type];
    const subscribers = this.listeners[type] || [];
    subscribers.forEach((cb) => cb(messages));
  }
}

发布者:

class Publisher {
  constructor(name, context) {

    this.name = name;


    this.context = context;

  }

  publish(type, content) {
    this.context.publish(type, content);
  }


}

订阅者:

class Subscriber {
  constructor(name, context) {

    this.name = name;


    this.context = context;

  }

  subscribe(type, cb) {
    this.context.subscribe(type, cb);
  }


}

使用:

const TYPE_A = 'music';
const TYPE_B = 'movie';
const TYPE_C = 'novel';

const pubsub = new PubSub();



const publisherA = new Publisher('publisherA', pubsub);
publisherA.publish(TYPE_A, 'we are young');
publisherA.publish(TYPE_B, 'the silicon valley');
const publisherB = new Publisher('publisherB', pubsub);
publisherB.publish(TYPE_A, 'stronger');
const publisherC = new Publisher('publisherC', pubsub);
publisherC.publish(TYPE_B, 'imitation game');

const subscriberA = new Subscriber('subscriberA', pubsub);
subscriberA.subscribe(TYPE_A, (res) => {
console.log('subscriberA received', res);
});

const subscriberB = new Subscriber('subscriberB', pubsub);
subscriberB.subscribe(TYPE_C, (res) => {
console.log('subscriberB received', res);
});
const subscriberC = new Subscriber('subscriberC', pubsub);
subscriberC.subscribe(TYPE_B, (res) => {
console.log('subscriberC received', res);
});

pubsub.notify(TYPE_A);
pubsub.notify(TYPE_B);
pubsub.notify(TYPE_C);

总的来说,订阅发布者模式是一种非常灵活和可扩展的设计模式,它被广泛应用于各种场合,比如:

  1. 事件发布者(例如 DOM 元素)调用 addEventListener 等方法来注册相关事件,监听点击、滚动、监听事件,将事件作为消息广播给所有订阅者。
  2. Vue 组件间通信可以用事件总线来实现,调用 $on()或者$emit()
// 创建事件总线实例
const EventBus = new Vue()

// 组件A中监听事件
EventBus.$on('event-name', (data) => {
  console.log('Component A received data:', data)
})


// 组件B中触发事件
EventBus.$emit('event-name', someData)
  1. Vue的双向绑定:

image.png

  1. nodejs里的常用的server也是基于订阅发布者模式
const { EventEmitter } = require("stream");
const server = http.createServer();
console.log(server instanceof EventEmiter);// true
// 常见的server的方法
const http = require('http');



const server = http.createServer();


// 监听服务器开始监听指定端口的事件
server.on('listening', () => {
  console.log('Server is listening at port 3000...');
});

// 监听客户端请求的事件,并进行处理
server.on('request', (req, res) => {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('<h1>Hello, World!</h1>');
});


// 监听服务器关闭的事件
server.on('close', () => {
  console.log('Server has been closed...');
});

server.listen(3000);


  1. 简而言之:解耦了组件间的耦合度,维护性强,因为并没有引用关系。

区别

两种设计模式思路是一样的,举个生活例子:

  • 观察者模式:某公司给自己员工发月饼发粽子,是由公司的行政部门发送的,这件事不适合交给第三方,原因是“公司”和“员工”是一个整体
  • 发布-订阅模式:某公司要给其他人发各种快递,因为“公司”和“其他人”是独立的,其唯一的桥梁是“快递”,所以这件事适合交给第三方快递公司解决

上述过程中,如果公司自己去管理快递的配送,那公司就会变成一个快递公司,业务繁杂难以管理,影响公司自身的主营业务,因此使用何种模式需要考虑什么情况两者是需要耦合的

两者区别如下图:

  • 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
  • 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
  • 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)

单例模式

单例模式只会在全局作用域下创建一次实例对象,让所有需要调用的地方都共享这一单例对象,如下图所示:

但是一般情况我们不认为全局变量是一个单例模式,原因是:

  • 全局命名污染
  • 不易维护,容易被重写覆盖
// 单例构造函数
function CreateSingleton (name) {
    this.name = name;


    this.getName();
};



// 获取实例的名字
CreateSingleton.prototype.getName = function() {
    console.log(this.name)
};
// 单例对象
const Singleton = (function(){
    var instance;
    return function (name) {
        if(!instance) {
            instance = new CreateSingleton(name);
        }
        return instance;
    }

})();

// 创建实例对象1
const a = new Singleton('a');
// 创建实例对象2
const b = new Singleton('b');


console.log(a===b); // true

应用场景:

  • Vuex使用单例模式可以创建一个全局的状态管理对象,用于统一管理应用的状态
  • 单例模式可以创建一个全局的缓存管理对象,确保统一的数据缓存策略
  • 在需要管理弹窗、提示框或对话框等组件的情况下,使用单例模式可以创建一个全局的弹窗管理对象

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYGs7kBb' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片