最近在重构一处功能,发现抽象组件竟然依赖了业务组件的代码,从window做用域call了业务组件提供的方法,这里的强耦合让我强迫症又犯了,本着天天进步一点点的原则,毅然决然的进行解耦合。app
咱们在学习面向对象的时候,有一条设计原则叫依赖倒置原则:dom
- A.高层次的模块不该该依赖于低层次的模块,他们都应该依赖于抽象。
- B.抽象不该该依赖于具体实现,具体实现应该依赖于抽象。
其实用通俗点的话来说,就是通用模块应该高冷,不该该为了某个页面的需求变更或某项功能的改变就要我专门对你进行适配,写一些偏业务的代码,好比一个电脑操做系统更新,兼容性问题通常不会由操做系统厂商来解决,都是软件开发商来适配更新。放到代码上也是同理,由于你不知道有多少业务模块回来使用你,因此要保持必要的抽象性。函数
简单描述下我遇到的问题,组件A实际上是为页面B服务的,可是大多数页面都须要经过组件A的功能进行操做,跳转到页面B,而后组件A再将刚才的操做告诉B以进行处理。因此这里以前我就写了依赖于具体的代码。每每很残酷的是遇到需求变动,这里处理可能要移动到C页面,或是增长D和E页面都要处理,因而就很尴尬了,难道每增长一个页面我就得去动A组件?因而我想到了观察者模式(不少又称发布-订阅模式)学习
A.js 组件Athis
// 用于存放订阅者的队列 const _MESSAGES = {}; /** * 向外提供一个用于订阅事件的函数 * @param type 订阅消息的类型 * @param fn 消息发生时的回调函数 */ export function register( type, fn ) { _MESSAGES[ type ] ? _MESSAGES[ type ].push( fn ) : _MESSAGES[ type ] = [ fn ]; } /** * 再提供提供一个用于发布事件的函数,组件可用,页面也能够用 * @param type 发布事件的类型 * @param backParama 发布事件时携带的参数 */ export function trigger( type, ...backParama ) { return _MESSAGES[ type ] && _MESSAGES[ type ].forEach( item => item.apply( this, backParama ) ); } // 而后是组件自己的一些操做作完后 // some code // 发布一个叫message的事件,并回传一个叫'hello world'的字符串 trigger( 'message', 'hello world!' );
B.js 页面B,依赖组件A操作系统
// 引入 import { register } from 'A'; // 组件A发布叫message的事件 register( 'message', function( data ) { // 在回调里处理咱们的事情 } );
这样咱们就完成了一个简单的订阅-发布模式,固然咱们还能够一步步对其进行完善,好比咱们要取消订阅,增长一个叫remove
的函数用于取消对抽象组件的消息订阅。设计
其实观察者模式在咱们代码中无处不在,好比咱们在多处对某个dom
元素进行事件绑定,事件发生后会按照订阅顺序对订阅者进行分发。code