浅谈前端的设计模式

131 阅读6分钟

前言

设计模式总的来说是一个抽象的概念,前人通过无数次的实践总结出的一套写代码的方式,通过这种方式写的代码可以让别人更加容易阅读、维护以及复用。

常见的设计模式:

MVC(Model-View-Controller)

image.png

Model(模型):数据层,负责存储数据。
View(视图):展现层,用户所看到的页面
Controller(控制器):协调层,负责协调Model和View,根据用户在View上的动作在Model上作出对应的更改,同时将更改的信息返回到View上。

三者之间的关系
Controller可以直接访问Model,也可以直接控制View,但是Model和View不能相互通信,相当于COntroller就是介于这两者之间的协调者。

MVP

image.png

MVP 模式与 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中使用观察者模式,来实现当 Model 层数据发生变化的时候,通知 View 层的更新。这样 View 层和 Model 层耦合在一起,当项目逻辑变得复杂的时候,可能会造成代码的混乱,并且可能会对代码的复用性造成一些问题。MVP 的模式通过使用 Presenter 来实现对 View 层和 Model 层的解耦。MVC 中的Controller 只知道 Model 的接口,因此它没有办法控制 View 层的更新,MVP 模式中,View 层的接口暴露给了 Presenter 因此可以在 Presenter 中将 Model 的变化和 View 的变化绑定在一起,以此来实现 View 和 Model 的同步更新。这样就实现了对 View 和 Model 的解耦,Presenter 还包含了其他的响应逻辑。

  • 由于 Presenter 层负责了数据获取、数据处理、交互逻辑、UI 效果等等功能,所以 Presenter 层就变得强大起来,相应的,Model 层只负责数据存储,而 View 层只负责视图,Model 和 View 层的责任纯粹而单一,如果我们需要添加或修改功能模块,只需要修改 Presenter 层就够了。由于 Presenter 层需要调用 View 层的方法更新视图,Presenter 层直接持有 View 层导致了 Presenter 对 View 的依赖。

正如上所说,更新视图需要 Presenter 层直接持有 View 层,并通过调用 View 层中的方法来实现,还是需要一系列复杂操作,有没有什么机制自动去更新视图而不用我们手动去更新呢,所以,MVVM 模式应运而生

MVVM(Model-View-ViewModel)

image.png

Model(模型):数据层,负责存储数据。
View(控制器):就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。
ViewModel(视图模型):就是View和Model层的粘合剂,封装业务逻辑处理,封装网络处理,封装数据缓存。就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。

优点:1. 耦合度更低 2. 更易数据一致性; 缺点:1. 数据绑定使bug易于传递,定位调试bug更困难 2. 数据绑定长期持有,对于model过大时,内存开销大

发布-订阅模式

发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。

class EventCenter {
  constructor() {
    // 1. 定义事件容器,用来装事件数组
    this.handlers = {};
  }

  // 2. 添加事件方法,参数:事件名 事件方法
  addEventListener(type, handler) {
    // 创建新数组容器
    if (!this.handlers[type]) {
      this.handlers[type] = [];
    }
    // 存入事件
    this.handlers[type].push(handler);
  }

  // 3. 触发事件,参数:事件名 事件参数
  dispatchEvent(type, params) {
    // 若没有注册该事件则抛出错误
    if (!this.handlers[type]) {
      return new Error("该事件未注册");
    }
    // 触发事件
    this.handlers[type].forEach((handler) => {
      handler(...params);
    });
  }

  // 4. 事件移除,参数:事件名 要删除事件,若无第二个参数则删除该事件的订阅和发布
  removeEventListener(type, handler) {
    if (!this.handlers[type]) {
      return new Error("事件无效");
    }
    if (!handler) {
      // 移除事件
      delete this.handlers[type];
    } else {
      const index = this.handlers[type].findIndex((el) => el === handler);
      if (index === -1) {
        return new Error("无该绑定事件");
      }
      // 移除事件
      this.handlers[type].splice(index, 1);
      if (this.handlers[type].length === 0) {
        delete this.handlers[type];
      }
    }
  }
}

观察者模式:

观察者模式模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主体对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

   //观察者类
    class Observe {
        constructor(name) {
           this.name = name
        }
        //观察的数据发生了变化,触发函数
        update(payload){
            console.log(`${this.name}观察的数据发生了变化:${payload}`)
        }
    }
    //被观察的类
    class Subject {
        constructor() {
            this.observeList = []
        }
        //添加观察者进观察者数组
        addObserve(observe){
            this.observeList.push(observe)
        }
        //发生变更通知观察者
        notify(payload){
            this.observeList.forEach(observe=>{
                observe.update(payload)
            })
        }
    }

发布-订阅模式和观察者模式区别

我们把这些差异快速总结一下:

在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。

观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。

关于疑问

有了MVC为什么还要有MVVM?

随着业务越来越复杂,数据解释这个环节会占比很高,而view层和model层一直都做不了这个事情,自然就必须由Controller层来完成,但是Controller层的定位也不是做数据解释的,轻量还可以,重量就会显得非常臃肿。继而诞生了MVVM,把modele层数据和view层数据对应关系指定起来。

React是MVVM框架吗?

React不是。它更倾向于是一个view层的库。

关于应用

Vue的数据劫持应用到了MVVM

image.png

总结

对多种设计模式的了解,可以帮助我们更好的提升自己以及在各种场景中使用。设计模式的具体选择使用还是要结合场景。