成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

DIP、IoC、DI、JS

ssshooter / 2977人閱讀

摘要:維基百科該原則規(guī)定高層次的模塊不應(yīng)該依賴與低層次的模塊,兩者都應(yīng)該依賴于抽象接口。依賴反轉(zhuǎn)原則則顛倒這種依賴關(guān)系,并以上面提到的兩個(gè)規(guī)定作為指導(dǎo)思想。維基百科這些話的意思就是將依賴對(duì)象的創(chuàng)建和綁定轉(zhuǎn)移到被依賴對(duì)象類的外部來(lái)實(shí)現(xiàn)。

在這個(gè)標(biāo)題中,除了 JS 是亂入之外,其它的幾個(gè)詞匯都是存在一個(gè)共同點(diǎn)的,那就是依賴。

那么,依賴是什么呢?

比如,現(xiàn)在我正在寫這篇博客文,但是我得在電腦上編輯,電腦便是我完成這件事的依賴。而在代碼中,最直觀的體現(xiàn)是模塊之間的依賴。如某個(gè)模塊依賴另外一個(gè)模塊,那么另外的那個(gè)模塊就是該模塊的依賴。其實(shí)在上篇博客文章《JaVaScript中的模塊》中,我們也手寫了一個(gè)模塊依賴管理器。

依賴這個(gè)理解起來(lái)很簡(jiǎn)單,但這不代表可以隨意的依賴。在寫模塊的時(shí)候,講究個(gè)高內(nèi)聚低耦合,以提高模塊的可拓展性和可維護(hù)性。模塊依賴了誰(shuí),怎么去依賴,都關(guān)乎了最終模塊的好與壞。

還好在編程界有著提高代碼質(zhì)量的金科玉律,我們可以用理論來(lái)指導(dǎo)實(shí)踐,寫出更好的代碼。

依賴反轉(zhuǎn)原則
依賴反轉(zhuǎn)原則(Dependency inversion principle,DIP),是一種特定的解耦形式,使得高層次的模塊不依賴于低層次的模塊的實(shí)現(xiàn)細(xì)節(jié),依賴關(guān)系被顛倒(反轉(zhuǎn)),從而使得低層次模塊依賴于高層次模塊的需求抽象?!?維基百科

該原則規(guī)定:

高層次的模塊不應(yīng)該依賴與低層次的模塊,兩者都應(yīng)該依賴于抽象接口。

抽象接口不應(yīng)該依賴于具體實(shí)現(xiàn)。而具體實(shí)現(xiàn)則應(yīng)該依賴于抽象接口。

現(xiàn)在用一個(gè)例子來(lái)解釋一波。

// Ajax.js
class Ajax {
  get() {
    return this.constructor.name;
  }
}
export default Ajax;

// main.js
import Ajax from "./Ajax";
class Main {
  constructor() {
    this.render()
  }
  render() {
    let content = (new Ajax()).get();
    console.log("content from", content);
  }
}
new Main();

剛開始的時(shí)候,我們基于 XMLHttpRequest 對(duì)象,封裝了 Ajax 用于請(qǐng)求數(shù)據(jù)。后來(lái) fetch 出來(lái)了,我們打算跟上時(shí)代的腳步,封裝 fetch 以取代 Ajax。

// Fetch.js
class Fetch {
  fetch() {
    return this.constructor.name;
  }
}
export default Fetch;

// main.js
import Fetch from "./Fetch";
class Main {
  constructor() {
    this.render();
  }
  render() {
    let content = (new Fetch()).fetch();
    console.log("content from", content);
  }
}
new Main();

從以上可以看出來(lái),整個(gè)替代過(guò)程很麻煩,我們需要找出封裝請(qǐng)求模塊(AjaxFetch)的所有引用,然后替換掉。又由于 Ajax、Fetch 的方法命名也是不同,所以也需要對(duì)應(yīng)地做更改。

這就是傳統(tǒng)的處理依賴關(guān)系的方式。在這里 Main 是高層次模塊,Ajax、Fetch 是低層次模塊。依賴關(guān)系創(chuàng)建于高層次模塊,且高層次模塊直接依賴低層次模塊,這種依賴關(guān)系限制了高層次模塊的復(fù)用性。

依賴反轉(zhuǎn)原則則顛倒這種依賴關(guān)系,并以上面提到的兩個(gè)規(guī)定作為指導(dǎo)思想。

// Service.js
class Service {
  request(){
    throw `${this.constructor.name} 沒有實(shí)現(xiàn) request 方法!`
  }
}
class Ajax extends Service {
  request(){
      return this.constructor.name;
  }
}
export default Ajax;

// Main.js
import Service from "./Service.js";
class Main {
  constructor() {
    this.render();
  }
  render() {
    let content = (new Service).request();
    console.log("content from", content);
  }
}
new Main();

在這里我們把共同依賴的 Service 作為抽象接口,它就是高層次模塊與低層次模塊需要共同遵守的契約。在高層次模塊中,它會(huì)默認(rèn) Service 會(huì)有 request 方法用來(lái)請(qǐng)求數(shù)據(jù)。在低層次模塊中,它會(huì)遵從 Service 復(fù)寫應(yīng)該存在的方法。這在《在JavaScript中嘗試組合模式》中,無(wú)論分支對(duì)象還是葉對(duì)象都實(shí)現(xiàn) expense() 方法的道理差不多。

即使后來(lái)需要封裝 axios 取代 fetch,我們也只需要在 Service.js 中修改即可。

再次回顧下傳統(tǒng)的依賴關(guān)系。

依賴關(guān)系創(chuàng)建于高層次模塊,且高層次模塊直接依賴低層次模塊。

經(jīng)過(guò)以上的折騰,我們充其量只是解決了高層次模塊直接依賴低層次模塊的問題。那么依賴關(guān)系創(chuàng)建于高層次模塊的問題呢?

控制反轉(zhuǎn)

如果說(shuō)依賴反轉(zhuǎn)原則告訴我們?cè)撘蕾囌l(shuí),那么控制反轉(zhuǎn)則告訴們誰(shuí)應(yīng)該來(lái)控制依賴。

像上面的 Main 模塊,它依賴 Service 模塊。為了獲得 Service 實(shí)例的引用,Main 在內(nèi)部靠自身 new 出了一個(gè) Service 實(shí)例。這樣明顯地引用其它模塊,無(wú)異加大了模塊間的耦合。

控制反轉(zhuǎn)(Inversion of Control,IoC),通過(guò)控制反轉(zhuǎn),對(duì)象在被創(chuàng)建的時(shí)候,有一個(gè)控制系統(tǒng)內(nèi)所有對(duì)象的外界實(shí)體,將其所依賴的對(duì)象的引用傳遞給它??梢哉f(shuō),依賴被注入到對(duì)象中。———— 維基百科

這些話的意思就是將依賴對(duì)象的創(chuàng)建和綁定轉(zhuǎn)移到被依賴對(duì)象類的外部來(lái)實(shí)現(xiàn)。實(shí)現(xiàn)控制反轉(zhuǎn)最常見的方式是依賴注入,還有一種方式依賴查找。

依賴注入
依賴注入(Dependency Injection,DI),在軟件工程中,依賴注入是種實(shí)現(xiàn)控制反轉(zhuǎn)用于解決依賴性設(shè)計(jì)模式。一個(gè)依賴關(guān)系指的是可被利用的一種對(duì)象(即服務(wù)提供端)。依賴注入是將所依賴的傳遞給將使用的從屬對(duì)象(即客戶端)。該服務(wù)將會(huì)變成客戶端的狀態(tài)的一部分。傳遞服務(wù)給客戶端,而非允許客戶端來(lái)建立或?qū)ふ曳?wù),是本設(shè)計(jì)模式的基本要求。

沒看懂?沒關(guān)系。這句話講的是,把過(guò)程放在外面,將結(jié)果帶入內(nèi)部。在《JaVaScript中的模塊》中,我們已經(jīng)用到過(guò)依賴注入,就是對(duì)于依賴模塊的模塊,則把依賴作為參數(shù)使用

所以我們?cè)俅胃脑煜拢?/p>

// Service.js
class Service {
  request() {
    throw `${this.constructor.name} 沒有實(shí)現(xiàn) request 方法!`
  }
}
class Ajax extends Service {
  request() {
    return this.constructor.name;
  }
}
export default Ajax;
// Main.js
class Main {
  constructor(options) {
    this.Service = options.Service;
    this.render();
  }
  render() {
    let content = this.Service.request();
    console.log("content from", content);
  }
}
export default Main;
// index.js
import Service from "./Service.js";
import Main from "./Main.js";
new Main({
  Service: new Service()
})

Main 模塊中, Service 的實(shí)例化是在外部完成,并在 index.js 中注入。相比上一次,改動(dòng)后的代碼并沒有看出帶來(lái)多大的好處。如果我們?cè)僭黾右粋€(gè)模塊呢?

class Router {
  constructor() {
    this.init();
  }
  init() {
    console.log("Router::init")
  }
}
export default Router;
# Main.js
+   this.Service = options.Router;

# index.js
+   import Router from "./Router.js"
    new Main({
+        Router: new Service()
    })

若是內(nèi)部實(shí)例化就不好處理了??蓳Q成依賴注入后,這個(gè)問題就很好解決了。

// utils.js
export const toOptions = params =>
  Object.entries(params).reduce((accumulator, currentValue) => {
    accumulator[currentValue[0]] = new currentValue[1]()
    return accumulator;
  }, {});

// Main.js
class Main {
  constructor(options) {
    Object.assign(this, options);
    this.render();
  }
  render() {
    let content = this.Service.request();
    console.log("content from", content);
  }
}
export default Main;

// index.js
import Service from "./Service.js";
import Router from "./Router.js";
import Main from "./Main.js";
import { toOptions } from "./utils.js"
/**
 * toOptions 轉(zhuǎn)換成參數(shù)形式
 * @params {Object} 類
 * @return {Object} {Service: Service實(shí)例, Router: Router實(shí)例}
 */
const options = toOptions({Service, Router});
new Main(options);

因?yàn)橐蕾囎⑷氚岩蕾嚨囊脧耐獠恳?,所以這里使用 Object.assign(this, options) 方式,把依賴全部加到了 this 上。即使再增加模塊,也只需要在 index.js 中引入即可。

到了這里,DIP、IoCDI 的概念應(yīng)該有個(gè)清晰的認(rèn)識(shí)了。然后我們?cè)俳Y(jié)合實(shí)際,加個(gè)功能再次鞏固以下。作為一功能個(gè)獨(dú)立的模塊,一般都有個(gè)初始化的過(guò)程。

現(xiàn)在我們要做的是遵守一個(gè)初始化的約定,定義一個(gè)抽象接口,

// Interface.js
export class Service {
  request() {
    throw `${this.constructor.name} 沒有實(shí)現(xiàn) request 方法!`
  }
}
export class Init {
  init() {
    throw `${this.constructor.name} 沒有實(shí)現(xiàn) init 方法!`
  }
}
// Service.js
import { Init, Service } from "./Interface.js";
import { mix } from "./utils.js"
class Ajax extends mix(Init, Service) {
  constructor() {
    super();
  }
  init() {
    console.log("Service::init")
  }
  request() {
    return this.constructor.name;
  }
}
export default Ajax;

Main、Service、Router 都依賴 Init 接口(在這里就是一種協(xié)定),Service 模塊比較特殊,所以做了 Mixin 處理。要做到統(tǒng)一初始化,Main 還需要做些事。

// Main.js
import { Init } from "./Interface.js"
class Main extends Init {
  constructor(options) {
    super();
    Object.assign(this, options);
    this.options = options;
    this.render();
  }
  init() {
    (Object.values(this.options)).map(item => item.init());
    console.log("Main::init");
  }
  render() {
    let content = this.Service.request();
    console.log("content from", content);
  }
}
export default Main;

至此,結(jié)束

// index.js
import Service from "./Service.js";
import Router from "./Router.js";
import Main from "./Main.js";
import { toOptions } from "./utils.js"

/**
 * toOptions
 * 轉(zhuǎn)換成參數(shù)形式
 * @params {Object} 類
 * @return {Object}
 * {
 *    Service: Service實(shí)例,
 *    Router: Router實(shí)例
 * }
 */
const options = toOptions({ Service, Router });

(new Main(options)).init();

//  content from Ajax
//  Service::init
//  Router::init
//  Main::init

(以上所有示例可見GitHub)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/109275.html

相關(guān)文章

  • OOD、DIP、IOC、DI、依賴注入容器(即 控制反轉(zhuǎn)容器,IOC Container)

    摘要:服務(wù)本省作為一個(gè)高層類,對(duì)外提供訪問,卻受制于提供具體服務(wù)的服務(wù)提供者定義的實(shí)現(xiàn),高層模塊依賴底層模塊實(shí)現(xiàn),違背了依賴倒置原則。遵循依賴倒置原則的例子場(chǎng)景同介紹中場(chǎng)景。 1. 名詞介紹 OOD,面向?qū)ο笤O(shè)計(jì) DIP,依賴倒置(軟件設(shè)計(jì)原則) IOC,控制反轉(zhuǎn)(軟件設(shè)計(jì)模式) DI,依賴注入 IOC Container,控制反轉(zhuǎn)容器,也是依賴注入容器 2. 組成部分 服務(wù)清單(功能...

    renweihub 評(píng)論0 收藏0
  • Yii源碼解讀-依賴注入(容器)

    摘要:在中使用解耦,有兩種注入方式構(gòu)造函數(shù)注入屬性注入。對(duì)象的實(shí)例化解析依賴信息該方法實(shí)質(zhì)上就是通過(guò)的反射機(jī)制,通過(guò)類的構(gòu)造函數(shù)的參數(shù)分析他所依賴的單元。 有關(guān)概念 依賴倒置原則(Dependence Inversion Principle, DIP) 傳統(tǒng)軟件設(shè)計(jì)中,上層代碼依賴于下層代碼,當(dāng)下層出現(xiàn)變動(dòng)時(shí),上層也要相應(yīng)變化。 DIP的核心思想是:上層定義接口,下層實(shí)現(xiàn)這個(gè)接口,從而使的下...

    Prasanta 評(píng)論0 收藏0
  • Yii2中的依賴注入

    摘要:構(gòu)造器注入實(shí)現(xiàn)特定參數(shù)的構(gòu)造函數(shù),在新建對(duì)象時(shí)傳入所依賴類型的對(duì)象。 基本概念 1.依賴倒置(反轉(zhuǎn))原則(DIP):一種軟件架構(gòu)設(shè)計(jì)的原則(抽象概念,是一種思想)在面向?qū)ο缶幊填I(lǐng)域中,依賴反轉(zhuǎn)原則(Dependency inversion principle,DIP)是指一種特定的解耦(傳統(tǒng)的依賴關(guān)系創(chuàng)建在高層次上,而具體的策略設(shè)置則應(yīng)用在低層次的模塊上)形式,使得高層次的模塊不依賴于...

    harriszh 評(píng)論0 收藏0
  • 搞懂依賴注入, 用 PHP 手寫簡(jiǎn)易 IOC 容器

    摘要:依賴注入控制反轉(zhuǎn)的一種具體實(shí)現(xiàn)方法。接下來(lái),我們使用依賴注入實(shí)現(xiàn)控制反轉(zhuǎn),使依賴關(guān)系倒置依賴被動(dòng)傳入。從單元測(cè)試的角度看,依賴注入更方便和操作,方便了測(cè)試人員寫出質(zhì)量更高的測(cè)試代碼。 前言 好的設(shè)計(jì)會(huì)提高程序的可復(fù)用性和可維護(hù)性,也間接的提高了開發(fā)人員的生產(chǎn)力。今天,我們就來(lái)說(shuō)一下在很多框架中都使用的依賴注入。 一些概念 要搞清楚什么是依賴注入如何依賴注入,首先我們要明確一些概念。 D...

    antz 評(píng)論0 收藏0
  • Laravel中的核心概念

    摘要:可以為服務(wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊(cè)之后調(diào)用。所有服務(wù)提供者都在配置文件中注冊(cè)??梢赃x擇推遲服務(wù)提供者的注冊(cè),直到真正需要注冊(cè)綁定時(shí),這樣可以提供應(yīng)用程序的性能。 本文最早發(fā)布于 Rootrl的Blog 導(dǎo)言 Laravel是一款先進(jìn)的現(xiàn)代化框架,里面有一些概念非常重要。在上手Laravel之前,我認(rèn)為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...

    ddongjian0000 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<