中介者设计模式——业务实践

栏目: 后端 · 发布时间: 6年前

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

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

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

实现原理

中介者设计模式——业务实践

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

废话不多说直接上代码;

// 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 消息,执行回调函数 ———— 输出 first
event.on('demo', () => {
  console.log('first');
});
// 订阅 demo 消息,执行回调函数 ———— 输出 second
event.on('demo', () => {
  console.log('second');
})

// 发布 demo 消息
event.emit('demo')
// first
// second
复制代码

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

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

中介者设计模式——业务实践

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

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

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

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

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

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

项目实践

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

中介者设计模式——业务实践

需求列表如下

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

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

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

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

const event = new EventEimtter();


const columns = [
  ...,
  {
    title: '操作',
    bodyRender: (rowData) => {
      return (
        <div>
          <Button onClick={() => {
            event.emit('page-decoration', rowData)
          }}> 桌位装修 </Button>
          <Button onClick={() => {
            event.emit('desk-manage', rowData)
          }}> 桌位装修 </Button>
          <Button onClick={() => {
            event.emit('action-setting', rowData)
          }}> 桌位装修 </Button>
        </div>
      );
    }
  },
  ....
]

// 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 (
      <Table columns={columns} ...props/>
    );
  }

  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

深耕业务开发与设计

中介者设计模式——业务实践

总结

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

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Windows内核原理与实现

Windows内核原理与实现

潘爱民 / 电子工业出版社 / 2010年4月 / 99.00元

本书从操作系统原理的角度,详细解析了Windows如何实现现代操作系统的各个关键部件,包括进程、线程、物理内存和虚拟内存的管理,Windows中的同步和并发性支持,以及Windows的I/O模型。在介绍这些关键部件时,本书直接以Windows的源代码(WRK, Windows Research Kernel)为参照,因而读者可以了解像Windows这样的复杂操作系统是如何在x86处理器上运行的。 ......一起来看看 《Windows内核原理与实现》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具