博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
中介者设计模式——业务实践
阅读量:7251 次
发布时间:2019-06-29

本文共 5308 字,大约阅读时间需要 17 分钟。

定义:中介者设计模式是通过中介对象封装一系列对象之间的交互,使对象之间不再相互引用,降低他们之间的耦合。

中介者设计模式和观察者设计模式一样,都是通过消息的收发机制实现的,在观察者模式中,一个对象既可以是消息的发送者也是消息的接收者,对象之间信息交流依托于消息系统实现解耦。而中介者模式中消息发送送方只有一个,就是中介对象,而且中介对象不能订阅消息,只有那些活跃对象(订阅者)才可订阅中介者的消息,简单的理解可以看作是将消息系统封装在中介者对象内部,所以中介者对象只能是消息的发送者。

实现原理

创建中介者对象(调度中心)

废话不多说直接上代码;

// eventeimtter.js// 创建中介者对象(调度中心)class EventEimtter {  constructor() {    // 创建消息对象    this.$event = {};  }  /**   * 检测消息对象是否存在,不存在则初始化该消息   * @param {*} event   */  checkEvent(event) {    if (!this.$event) {      this.$event = {};    }    if (!this.$event[event]) {      this.$event[event] = [];    }  }  /**   * 订阅消息   * @param {*} type 消息类型   * @param {*} action   * @param {*} context 消息作用域上下文   */  on(type, action, context = null) {    this.checkEvent(type);    this.$event[type].push(action.bind(context));    return this;  }  /**   * 发送消息   * @param {*} type   * @param  {...any} args   */  emit(type, ...args) {    if (!this.$event[type]) {      this.$event[type] = [];    }    this.$event[type].forEach(func => {      func(...args);    });    return this;  }  /**   * 仅能发送一次   * @param {*} type   * @param {*} action   * @param {*} scope 作用域   */  once(type, action, scope = null) {    this.checkEvent(type);    const newfn = (...args) => {      this.off(type, action);      action.call(scope, ...args);    };    this.on(type, newfn);    return this;  }  /**   * 移除已经订阅的消息   * @param {*} type   * @param {*} action   */  off(type, action) {    const $event = this.$event[type];    if ($event) {      for (let i in $event) {        if ($event[i] === action) {          $event.splice(i, 1);          break;        }      }      if (!$event.length) {        delete this.$event[type];      }    }    return this;  }  /**   * 移除某个的类型消息   * @param {*} type   */  removeListener(type) {    delete this.$event[type];    return this;  }  /**   * 移除所有订阅消息   */  removeAllListener() {    this.$event = null;    return this;  }  /**   * 获取所有的消息类型   */  getEvent() {    return this.$event;  }}export default EventEimtter;复制代码

小试牛刀,可否一用

在这里,我只需要订阅两个消息,然后让中介者发布;看看是否能够发布成功。

//单元测试import EventEimtter from './eventeimtter';const event = new EventEimtter();// 订阅 demo 消息,执行回调函数 ———— 输出 firstevent.on('demo', () => {  console.log('first');});// 订阅 demo 消息,执行回调函数 ———— 输出 secondevent.on('demo', () => {  console.log('second');})// 发布 demo 消息event.emit('demo')// first// second复制代码

业务价值的产生,实际开发中的实践

先说痛点,在实际的项目开发中一个页面 js 可能有十几个 class 类;你所见到的代码会是这样的。

以上代码中,可以看出一个 React 组件,完全不见 React 周期函数,类函数过多 ,render 函数过于庞大;监听的方法也很多,阅读,维护,迭代成功过高。这段代码不管是对于开发者本身还是维护者,都不友好;迫切需要代码拆分,且实现结构层次清晰。

然而实际开发中,业务变更、迭代过快,有的业务本身复杂度极高,一个项目经手人也很多。如果代码不整洁,后来人就很难看懂,人们往往会对难以看懂的代码失去耐心,不愿意进一步了解。如果不能进一步了解一部分代码,也就难以改进它,这样的后果可能有两点:

  • 重构,代码被抛弃
  • 直接复制这段代码在别的地方使用

下面是我站在前端的角度去思考业务:

  1. 业务数据:负责获取业务数据
  2. 业务逻辑:实现产品所定义的规则
  3. 逻辑数据:通过一系列规则所产出的逻辑数据
  4. 视图数据:通过逻辑数据转换成视图数据(不将逻辑和视图直接绑定)
  5. 视图展示:通过视图数据,直接驱动视图层展示对应视图
  6. 视图功能:通过视图展示组装成的需求功能

在简单的业务需求中,可能我拿到的后端数据,就直接可以去渲染视图层,然后就完善功能。从开发的成本和复杂度上考量上,是不值得去做业务拆分。所以,在复杂的业务需求中以及兼顾拆分和维护中,这种业务方法论就可以大展手脚了。以下,我就拿开头的例子,详细解析围绕业务的6大部分的设计。

项目实践

我始终坚信技术的价值是在业务中产生的,技术本身是没有价值的,技术的价值取决于是否能在项目中落地以及解决业务的痛点。作为中介者模式在项目中的落地,先举一个小栗子!

需求列表如下
  • 一个分页表格, 分别有网点名称、网点地址、联系电话、操作栏四列。
  • 每一行操作栏有三个按钮,分别是 桌位管理、页面装修、功能设置

一般要求:使用 分页表格 组件,配置好 columns ,操作栏定制渲染;更加简易的拓展以及敏捷的操作,当然维护和开发的成本也需要考虑的。

使用 zent table 组件开发,受益于 React 数据驱动的思想,columns 是以 props 传入;columns 中的定制渲染,可能需要涉及到父子组件之间的通信。

在正常的开发中,我们可以这么做。

const event = new EventEimtter();const columns = [  ...,  {    title: '操作',    bodyRender: (rowData) => {      return (        
); } }, ....]// Action 消息处理函数实体类,业务逻辑源码class Action { handlerPageDecoration() { ... } handlerDeskManage() { ... } handlerActionSetting() { ... }}const action = new Action()class Demo extends Component { componentWillMount() { // 订阅消息 event.on('page-decoration', action.handlerPageDecoration, this) event.on('desk-manage', action.handlerDeskManage, this) event.on('action-setting', action.handlerActionSetting, this) } render() { return (
); } componentWillUnmount() { // 当该组件销毁时,取消所以监听事件;否则内存会炸掉 event.removeAllListener(); }}复制代码
生命周期的使用时机

React 生命周期

  • constructor:尽量简洁,只做最基本的 state 初始化
  • willMount: 一些内部使用变量的初始化
  • render: 触发非常频繁,尽量只做渲染相关的事情
  • didMount: 一些不影响初始化的操作应在这里完成,比如根据浏览器不同进行操作,ajax获取数据,监听 document 事件等(server render)。
  • willUnmount:销毁操作,销毁计时器、销毁自己的事件监听等
  • willReceiveProps: 当有 props 做 state 时,监听 props 的变化去改变 state,在这个生命周期里 setState 不会触发两次渲染
  • shouldComponentUpdate:手动判断组件是否应该更新,避免因为页面更新做成的无谓更新,组件的重点优化之一。
  • willUpdate:在 state 变化后如果需要修改一些变量,可以在这里执行
  • didUpdate: 与 didMount 类似,进行一些不影响到 render 的操作, update 相关的生命周期里最好不要做 setState 操作,否则容易造成死循环。
在 React 生命周期中,实践业务数据转换

业务数据的来源:

  • ReactCompoent 在 willMount 时,初始化的 state、props中获取
  • didMount 时 Ajax 获取的数据 业务逻辑(业务规则):
  • 处理业务规则的源码,根据不同的规则,对业务数据进行处理
  • 产生逻辑数据
  • 需要在 constructor  或者 willMount  中完成业务逻辑的订阅 逻辑数据:
  • 使用业务逻辑处理产生,同步到视图数据 试图数据:
  • 同步逻辑数据的,中间可加 hook 视图展示:
  • 根据视图数据单项 render

深耕业务开发与设计

总结

同观察者模式一样,中介者模式的主要业务也是通过模块间或者对象间的复杂通信,来解决模块间或对象的耦合。对于中介者对象的本质是分装多个对象的交互,并且这些对象的交互一般都是中介者内部实现的。

与外观模式的封装特性相比,中介者模式对多个对象的交互封装,且这些对象一般处于同一层面上,并且封装的交互在中介者内部,而外观模式封装的目的是为了提供更简单的易用接口,而不会添加其他功能。

转载于:https://juejin.im/post/5bdd599ee51d45069d739cf9

你可能感兴趣的文章
php 5.6.11添加模块
查看>>
matlab-线性代数 根据二次型写矩阵
查看>>
Win 7 deskhelp
查看>>
Sybase SQL Anywhere 7 数据库修复成功
查看>>
发展到1Gbps及其以上的速度
查看>>
TurboMail为企业提供海量投递邮件群发系统
查看>>
Linux系统命令Cut使用
查看>>
我的友情链接
查看>>
chrome 跨域设置-(完善博客内容)
查看>>
12月8日学习内容整理:ORM中的创建多表关系,基于多表关系的插入记录,基于对象的跨表查询...
查看>>
利用nginx实现负载均衡
查看>>
【EXCRT模板】POJ2891/LuoGu4777Strange Way to Express Integers拓展中国剩余定理
查看>>
Python基础24_正则表达式,re模块,
查看>>
mysql 开源 ~ canal+otter系列(2)
查看>>
跟我一起写 Makefile (Linux )
查看>>
CC2640R2F&TI-RTOS 拿到 TI CC2640R2F 开发板 第二件事就是 LED 驱动 ,点个灯
查看>>
ELASTIC SEARCH 性能调优
查看>>
Java并发总结(三):中断线程
查看>>
Beer Refrigerator
查看>>
hadoop输入分片计算(Map Task个数的确定)
查看>>