使用觀察者模式(發佈-訂閱模式)對抽象組件與業務組件進行解耦

最近在重構一處功能,發現抽象組件竟然依賴了業務組件的代碼,從window做用域call了業務組件提供的方法,這裏的強耦合讓我強迫症又犯了,本着天天進步一點點的原則,毅然決然的進行解耦合。app

咱們在學習面向對象的時候,有一條設計原則叫依賴倒置原則dom

  • A.高層次的模塊不該該依賴於低層次的模塊,他們都應該依賴於抽象。
  • B.抽象不該該依賴於具體實現,具體實現應該依賴於抽象。

其實用通俗點的話來說,就是通用模塊應該高冷,不該該爲了某個頁面的需求變更或某項功能的改變就要我專門對你進行適配,寫一些偏業務的代碼,好比一個電腦操做系統更新,兼容性問題通常不會由操做系統廠商來解決,都是軟件開發商來適配更新。放到代碼上也是同理,由於你不知道有多少業務模塊回來使用你,因此要保持必要的抽象性。函數

簡單描述下我遇到的問題,組件A實際上是爲頁面B服務的,可是大多數頁面都須要經過組件A的功能進行操做,跳轉到頁面B,而後組件A再將剛纔的操做告訴B以進行處理。因此這裏以前我就寫了依賴於具體的代碼。每每很殘酷的是遇到需求變動,這裏處理可能要移動到C頁面,或是增長D和E頁面都要處理,因而就很尷尬了,難道每增長一個頁面我就得去動A組件?因而我想到了觀察者模式(不少又稱發佈-訂閱模式)學習

  1. 組件A與頁面B撇清關係
  2. 組件A來發佈一個消息,我管你接收不接收?反正我正常發出去了
  3. 頁面B甚至C、D、E、F...來訂閱這個消息以進行處理
  4. 那麼這樣就完成了組件與具體實現的解耦
    下面是實現的代碼

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

相關文章
相關標籤/搜索