在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ S.O.L.I.D 五大原則之依賴(lài)倒置原則 DIP
代碼復(fù)用模式(避免篇)
S.O.L.I.D 五大原則之接口隔離原則 ISP
設(shè)計(jì)模式之狀態(tài)模式
JavaScript 核心(晉級(jí)高手必讀篇)
設(shè)計(jì)模式之建造者模式
JavaScript 與 DOM(上)——也適用于新手
設(shè)計(jì)模式之中介者模式
設(shè)計(jì)模式之裝飾者模式
設(shè)計(jì)模式之模板方法
設(shè)計(jì)模式之外觀模式
強(qiáng)大的原型和原型鏈
設(shè)計(jì)模式之構(gòu)造函數(shù)模式
揭秘命名函數(shù)表達(dá)式
深入理解J avaScript 系列(結(jié)局篇)
執(zhí)行上下文(Execution Contexts)
函數(shù)(Functions)
《你真懂 JavaScript 嗎?》答案詳解
設(shè)計(jì)模式之適配器模式
設(shè)計(jì)模式之組合模式
設(shè)計(jì)模式之命令模式
S.O.L.I.D 五大原則之單一職責(zé) SRP
編寫(xiě)高質(zhì)量 JavaScript 代碼的基本要點(diǎn)
求值策略
閉包(Closures)
對(duì)象創(chuàng)建模式(上篇)
This? Yes,this!
設(shè)計(jì)模式之代理模式
變量對(duì)象(Variable Object)
S.O.L.I.D 五大原則之里氏替換原則 LSP
面向?qū)ο缶幊讨话憷碚?/span>
設(shè)計(jì)模式之單例模式
Function 模式(上篇)
S.O.L.I.D 五大原則之依賴(lài)倒置原則 DIP
設(shè)計(jì)模式之迭代器模式
立即調(diào)用的函數(shù)表達(dá)式
設(shè)計(jì)模式之享元模式
設(shè)計(jì)模式之原型模式
根本沒(méi)有“JSON 對(duì)象”這回事!
JavaScript 與 DOM(下)
面向?qū)ο缶幊讨?ECMAScript 實(shí)現(xiàn)
全面解析 Module 模式
對(duì)象創(chuàng)建模式(下篇)
設(shè)計(jì)模式之職責(zé)鏈模式
S.O.L.I.D 五大原則之開(kāi)閉原則 OCP
設(shè)計(jì)模式之橋接模式
設(shè)計(jì)模式之策略模式
設(shè)計(jì)模式之觀察者模式
代碼復(fù)用模式(推薦篇)
作用域鏈(Scope Chain)
Function 模式(下篇)
設(shè)計(jì)模式之工廠模式

S.O.L.I.D 五大原則之依賴(lài)倒置原則 DIP

前言

本章我們要講解的是 S.O.L.I.D 五大原則 JavaScript 語(yǔ)言實(shí)現(xiàn)的第5篇,依賴(lài)倒置原則 LSP(The Dependency Inversion Principle )。

依賴(lài)倒置原則

依賴(lài)倒置原則的描述是:

  • 高層模塊不應(yīng)該依賴(lài)于低層模塊,二者都應(yīng)該依賴(lài)于抽象
  • 抽象不應(yīng)該依賴(lài)于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴(lài)于抽象

依賴(lài)倒置原則的最重要問(wèn)題就是確保應(yīng)用程序或框架的主要組件從非重要的底層組件實(shí)現(xiàn)細(xì)節(jié)解耦出來(lái),這將確保程序的最重要的部分不會(huì)因?yàn)榈蛯哟谓M件的變化修改而受影響。

該原則的第一部分是關(guān)于高層模塊和低層模塊之間的耦合方式,在傳統(tǒng)的分成架構(gòu)中,高層模塊(封裝了程序的核心業(yè)務(wù)邏輯)總依賴(lài)于低層的一些模塊(一些基礎(chǔ)點(diǎn))。當(dāng)應(yīng)用依賴(lài)倒置原則的時(shí)候,關(guān)系就反過(guò)來(lái)了。和高層模塊依賴(lài)于低層模塊不同,依賴(lài)倒置是讓低層模塊依賴(lài)于高層模塊里定義的接口。舉例來(lái)說(shuō),如果要給程序進(jìn)行數(shù)據(jù)持久化,傳統(tǒng)的設(shè)計(jì)是核心模塊依賴(lài)于一個(gè)持久化模塊的API,而根據(jù)依賴(lài)倒置原則重構(gòu)以后,則是核心模塊需要定義持久化的 API 接口,然后持久化的實(shí)現(xiàn)實(shí)例需要實(shí)現(xiàn)核心模塊定義的這個(gè) API 接口。

該原則的第二部分描述的是抽象和細(xì)節(jié)之間的正確關(guān)系。理解這一部分,通過(guò)了解 C++ 語(yǔ)言比較有幫助,因?yàn)樗倪m用性比較明顯。

不像一些靜態(tài)類(lèi)型的語(yǔ)言,C++沒(méi)有提供一個(gè)語(yǔ)言級(jí)別的概念來(lái)定義接口,那類(lèi)定義和類(lèi)實(shí)現(xiàn)之間到底是怎么樣的呢,在 C++里,類(lèi)通過(guò)頭文件的形式來(lái)定義,其中定義了源文件需要實(shí)現(xiàn)的類(lèi)成員方法和變量。因?yàn)樗械淖兞亢退接蟹椒ǘ级x在頭文件里,所以可以用來(lái)抽象以便和實(shí)現(xiàn)細(xì)節(jié)之前解耦出來(lái)。通過(guò)定只定義抽象方法來(lái)實(shí)現(xiàn)(C++里是抽象基類(lèi))接口這個(gè)概念用于實(shí)現(xiàn)類(lèi)來(lái)實(shí)現(xiàn)。

DIP and JavaScript

因?yàn)?JavaScript 是動(dòng)態(tài)語(yǔ)言,所以不需要去為了解耦而抽象。所以抽象不應(yīng)依賴(lài)于細(xì)節(jié)這個(gè)改變?cè)?JavaScript 里沒(méi)有太大的影響,但高層模塊不應(yīng)依賴(lài)于低層模塊卻有很大的影響。

在當(dāng)靜態(tài)類(lèi)型語(yǔ)言的上下文里討論依賴(lài)倒置原則的時(shí)候,耦合的概念包括語(yǔ)義(semantic)和物理(physical)兩種。這就是說(shuō),如果一個(gè)高層模塊依賴(lài)于一個(gè)低層模塊,也就是不僅耦合了語(yǔ)義接口,也耦合了在底層模塊里定義的物理接口。也就是說(shuō)高層模塊不僅要從第三方類(lèi)庫(kù)解耦出來(lái),也需要從原生的低層模塊里解耦出來(lái)。

為了解釋這一點(diǎn),想象一個(gè).NET程序可能包含一個(gè)非常有用的高層模塊,而該模塊依賴(lài)于一個(gè)低層的持久化模塊。當(dāng)作者需要在持久化 API 里增加一個(gè)類(lèi)似的接口的時(shí)候,不管依賴(lài)倒置原則有沒(méi)有使用,高層模塊在不重新實(shí)現(xiàn)這個(gè)低層模塊的新接口之前是沒(méi)有辦法在其它的程序里得到重用的。

在 JavaScript 里,依賴(lài)倒置原則的適用性?xún)H僅限于高層模塊和低層模塊之間的語(yǔ)義耦合,比如,DIP 可以根據(jù)需要去增加接口而不是耦合低層模塊定義的隱式接口。

為了來(lái)理解這個(gè),我們看一下如下例子:

$.fn.trackMap = function(options) {
    var defaults = {
        /* defaults */
    };
    options = $.extend({}, defaults, options);
    var mapOptions = {
        center: new google.maps.LatLng(options.latitude,options.longitude),
        zoom: 12,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    },
        map = new google.maps.Map(this[0], mapOptions),
        pos = new google.maps.LatLng(options.latitude,options.longitude);
    var marker = new google.maps.Marker({
        position: pos,
        title: options.title,
        icon: options.icon
    });
    marker.setMap(map);
    options.feed.update(function(latitude, longitude) {
        marker.setMap(null);
        var newLatLng = new google.maps.LatLng(latitude, longitude);
        marker.position = newLatLng;
        marker.setMap(map);
        map.setCenter(newLatLng);
    });
    return this;
};
var updater = (function() {
    // private properties
    return {
        update: function(callback) {
            updateMap = callback;
        }
    };
})();
$("#map_canvas").trackMap({
    latitude: 35.044640193770725,
    longitude: -89.98193264007568,
    icon: 'http://bit.ly/zjnGDe',
    title: 'Tracking Number: 12345',
    feed: updater
});

在上述代碼里,有個(gè)小型的 JS 類(lèi)庫(kù)將一個(gè) DIV 轉(zhuǎn)化成 Map 以便顯示當(dāng)前跟蹤的位置信息。trackMap 函數(shù)有 2 個(gè)依賴(lài):第三方的 Google Maps API 和 Location feed。該 feed 對(duì)象的職責(zé)是當(dāng) icon 位置更新的時(shí)候調(diào)用一個(gè) callback 回調(diào)(在初始化的時(shí)候提供的)并且傳入緯度 latitude 和精度 longitude。Google Maps API 是用來(lái)渲染界面的。

feed 對(duì)象的接口可能按照裝,也可能沒(méi)有照裝 trackMap 函數(shù)的要求去設(shè)計(jì),事實(shí)上,他的角色很簡(jiǎn)單,著重在簡(jiǎn)單的不同實(shí)現(xiàn),不需要和 Google Maps 這么依賴(lài)。介于 trackMap 語(yǔ)義上耦合了 Google Maps API,如果需要切換不同的地圖提供商的話那就不得不對(duì) trackMap 函數(shù)進(jìn)行重寫(xiě)以便可以適配不同的 provider。

為了將于 Google maps 類(lèi)庫(kù)的語(yǔ)義耦合翻轉(zhuǎn)過(guò)來(lái),我們需要重寫(xiě)設(shè)計(jì) trackMap 函數(shù),以便對(duì)一個(gè)隱式接口(抽象出地圖提供商 provider 的接口)進(jìn)行語(yǔ)義耦合,我們還需要一個(gè)適配 Google Maps API 的一個(gè)實(shí)現(xiàn)對(duì)象,如下是重構(gòu)后的 trackMap 函數(shù):

$.fn.trackMap = function(options) {
    var defaults = {
        /* defaults */
    };
    options = $.extend({}, defaults, options);
    options.provider.showMap(
        this[0],
        options.latitude,
        options.longitude,
        options.icon,
        options.title);
    options.feed.update(function(latitude, longitude) {
        options.provider.updateMap(latitude, longitude);
    });
    return this;
};
$("#map_canvas").trackMap({
    latitude: 35.044640193770725,
    longitude: -89.98193264007568,
    icon: 'http://bit.ly/zjnGDe',
    title: 'Tracking Number: 12345',
    feed: updater,
    provider: trackMap.googleMapsProvider
});

在該版本里,我們重新設(shè)計(jì)了 trackMap 函數(shù)以及需要的一個(gè)地圖提供商接口,然后將實(shí)現(xiàn)的細(xì)節(jié)挪到了一個(gè)單獨(dú)的 googleMapsProvider 組件,該組件可能獨(dú)立封裝成一個(gè)單獨(dú)的 JavaScript 模塊。如下是我的 googleMapsProvider 實(shí)現(xiàn):

trackMap.googleMapsProvider = (function() {
    var marker, map;
    return {
        showMap: function(element, latitude, longitude, icon, title) {
            var mapOptions = {
                center: new google.maps.LatLng(latitude, longitude),
                zoom: 12,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            },
                pos = new google.maps.LatLng(latitude, longitude);
            map = new google.maps.Map(element, mapOptions);
            marker = new google.maps.Marker({
                position: pos,
                title: title,
                icon: icon
            });
            marker.setMap(map);
        },
        updateMap: function(latitude, longitude) {
            marker.setMap(null);
            var newLatLng = new google.maps.LatLng(latitude,longitude);
            marker.position = newLatLng;
            marker.setMap(map);
            map.setCenter(newLatLng);
        }
    };
})();

做了上述這些改變以后,trackMap 函數(shù)將變得非常有彈性了,不必依賴(lài)于 Google Maps API,相反可以任意替換其它的地圖提供商,那就是說(shuō)可以按照程序的需求去適配任何地圖提供商。

何時(shí)依賴(lài)注入?

有點(diǎn)不太相關(guān),其實(shí)依賴(lài)注入的概念經(jīng)常和依賴(lài)倒置原則混在一起,為了澄清這個(gè)不同,我們有必要來(lái)解釋一下:

依賴(lài)注入是控制反轉(zhuǎn)的一個(gè)特殊形式,反轉(zhuǎn)的意思一個(gè)組件如何獲取它的依賴(lài)。依賴(lài)注入的意思就是:依賴(lài)提供給組件,而不是組件去獲取依賴(lài),意思是創(chuàng)建一個(gè)依賴(lài)的實(shí)例,通過(guò)工廠去請(qǐng)求這個(gè)依賴(lài),通過(guò) Service Locator 或組件自身的初始化去請(qǐng)求這個(gè)依賴(lài)。依賴(lài)倒置原則和依賴(lài)注入都是關(guān)注依賴(lài),并且都是用于反轉(zhuǎn)。不過(guò),依賴(lài)倒置原則沒(méi)有關(guān)注組件如何獲取依賴(lài),而是只關(guān)注高層模塊如何從低層模塊里解耦出來(lái)。某種意義上說(shuō),依賴(lài)倒置原則是控制反轉(zhuǎn)的另外一種形式,這里反轉(zhuǎn)的是哪個(gè)模塊定義接口(從低層里定義,反轉(zhuǎn)到高層里定義)。

總結(jié)

這是五大原則的最后一篇了,在這 5 篇文字里我們看到了 SOLID 如何在 JavaScript 里實(shí)現(xiàn)的,不同的原則在 JavaScript 里通過(guò)不同的角度來(lái)說(shuō)明的。(大叔注:其實(shí)大叔覺(jué)得雖然是有點(diǎn)不倫不類(lèi),但從另外一個(gè)層面上說(shuō),大體的原則在各種語(yǔ)言上其實(shí)還是一樣的。)