深入理解JavaScript系列(22):S.O.L.I.D五大原則之依賴倒置原則DIP詳解_基礎知識
前言
本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實作的第5篇,依賴倒置原則LSP(The Dependency Inversion Principle )。
英文原文:http://freshbrewedcode.com/derekgreer/2012/01/22/solid-javascript-the-dependency-inversion-principle/
依賴倒置原則
依賴倒置原則的描述是:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
高層模組不應該依賴低層模組,二者都應該依賴抽象
B. Abstractions should not depend upon details. Details should depend upon abstractions.
抽像不應該依賴細節,細節應該依賴抽象
依賴倒置原則的最重要問題是確保應用程式或框架的主要元件從非重要的底層元件實現細節解耦出來,這將確保程式的最重要的部分不會因為低層次元件的變化修改而受影響。
這個原則的第一部分是關於高層模組和低層模組之間的耦合方式,在傳統的分成架構中,高層模組(封裝了程式的核心業務邏輯)總是依賴低層的一些模組(一些基礎點)。當應用依賴倒置原則的時候,關係就反過來了。和高層模組依賴低層模組不同,依賴倒置是讓低層模組依賴高層模組裡定義的介面。舉例來說,如果要給程式進行資料持久化,傳統的設計是核心模組依賴一個持久化模組的API,而根據依賴倒置原則重構以後,則是核心模組需要定義持久化的API接口,然後持久化的實作實例需要實作核心模組定義的這個API介面。
原則的第二部分描述的是抽象和細節之間的正確關係。理解這一部分,透過了解C 語言比較有幫助,因為他的適用性比較明顯。
不像一些靜態類型的語言,C 沒有提供一個語言級別的概念來定義接口,那類定義和類實現之間到底是怎麼樣的呢,在C 裡,類通過頭文件的形式來定義,其中定義了原始檔案需要實作的類別成員方法和變數。因為所有的變數和私有方法都定義在頭檔裡,所以可以用來抽象化以便和實作細節之前解耦出來。透過定只定義抽象方法來實作(C 裡是抽象基底類別)介面這個概念用來實作類別來實作。
DIP and JavaScript
因為JavaScript是動態語言,所以不需要去為了解耦而抽象化。所以抽像不應依賴細節這個改變在JavaScript裡沒有太大的影響,但高層模組不應該依賴低層模組卻有很大的影響。
在當靜態型別語言的脈絡裡討論依賴倒置原則的時候,耦合的概念包括語意(semantic)和物理(physical)兩種。這就是說,如果一個高層模組依賴於一個低層模組,也就是不僅耦合了語義接口,也耦合了在底層模組裡定義的物理接口。也就是說高層模組不只從第三方類別庫解耦出來,也需要從原生的低層模組解耦出來。
為了解釋這一點,想像一個.NET程式可能包含一個非常有用的高層模組,而這個模組依賴一個低層的持久化模組。當作者需要在持久化API裡增加一個類似的介面的時候,不管依賴倒置原則有沒有使用,高層模組在不重新實作這個低層模組的新介面之前是沒有辦法在其它的程式裡得到重用的。
在JavaScript裡,依賴倒置原則的適用性僅限於高層模組和低層模組之間的語意耦合,例如,DIP可以根據需要去增加介面而不是耦合低層模組定義的隱式介面。
為了來理解這個,我們看一下如下例子:
$.fn.trackMap = 函數(選項) {
var 預設值 = {
/* 預設值 */
};
選項 = $.extend({}, 預設值, 選項);
var mapOptions = {
中心:新的 google.maps.LatLng(options.latitude,options.longitude),
縮放:12,
地圖類型 ID:google.maps.MapTypeId.ROADMAP
},
地圖 = new google.maps.Map(this[0], mapOptions),
pos = new google.maps.LatLng(options.latitude,options.longitude);
var mark = new google.maps.Marker({
地點: pos,
標題:選項.標題,
圖示:選項.icon
});
marker.setMap(map);
options.feed.update(function(緯度, 經度) {
marker.setMap(null);
var newLatLng = new google.maps.LatLng(緯度, 經度);
標記.position = newLatLng;
marker.setMap(地圖);
map.setCenter(newLatLng);
});
回此;
};
var updater = (function() {
// 私有屬性
回 {
更新:函數(回呼){
updateMap = 回調;
}
};
})();
$("#map_canvas").trackMap({
緯度: 35.044640193770725,
經度:-89.98193264007568,
圖示:'http://bit.ly/zjnGDe',
title: '追蹤號碼:12345',
摘要:更新器
});
在上述程式碼裡,一個小型的JS類別庫將一個DIV轉換成Map以便顯示目前追蹤的位置資訊。 trackMap函數有2個依賴:第三方的Google Maps API和Location feed。該feed物件的職責是當圖示位置更新的時候調用一個callback回呼(在初始化的時候提供的)並確定緯度緯度和精度經度。 Google Maps API是用來渲染介面的。
feed物件的介面可能遵循安裝,也可能沒有照裝trackMap函數的要求去設計,事實上,他的角色很簡單,形式在簡單的不同實現,不需要和Google Maps這麼依賴。採集上耦合了Google Maps API,如果需要切換不同的地圖那麼就必須對trackMap函數進行重構以便可以改裝不同的provider。
為了實現Google地圖類別庫的介面連接對接過來,我們需要重寫設計trackMap函數,以便對一個隱式介面(抽像出地圖式提供者的介面)進行介面連接,我們還需要一個介面連接Google Maps API的一個實作對象,如下是重構後的trackMap函數:
$.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
});
在該版本裡,我們重新設計了trackMap函數以及需要的一個地圖提供者接口,然後將實現的細節挪到了一個單獨的googleMapsProvider元件,該元件可能獨立封裝成一個單獨的JavaScript模組。如下是我的googleMapsProvider實作:
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函數將變得非常有彈性了,不必依賴Google Maps API,相反可以任意替換其它的地圖提供商,那就是說可以按照程式的需求去適配任何地圖提供者。
何時依賴注入?
有點不太相關,其實依賴注入的概念常常和依賴倒置原則混在一起,為了澄清這個不同,我們有必要來解釋一下:
依賴注入是控制反轉的一個特殊形式,反轉的意思一個元件如何取得它的依賴。依賴注入的意思是:依賴提供給元件,而不是元件去獲取依賴,意思是建立一個依賴的實例,透過工廠去請求這個依賴,透過Service Locator或元件自身的初始化去請求這個依賴。依賴倒置原則和依賴注入都是關注依賴,而且都是用來反轉。不過,依賴倒置原則沒有關注元件如何獲取依賴,而是只關注高層模組如何從低層模組解耦出來。某種意義上說,依賴倒置原則是控制反轉的另一種形式,這裡反轉的是哪個模組定義介面(從低層定義,反轉到高層定義)。
總結
這是五大原則的最後一篇了,在這5篇文字裡我們看到了SOLID如何在JavaScript裡實現的,不同的原則在JavaScript里通過不同的角度來說明的。 (大叔註:其實大叔覺得雖然是有點不倫不類,但從另一個層面上說,大體的原則在各種語言上其實還是一樣的。)

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

SOLID原則是物件導向程式設計模式中的一組指導原則,旨在提高軟體設計的品質和可維護性。由羅伯特·馬丁(RobertC.Martin)提出,SOLID原則包括:單一職責原則(SingleResponsibilityPrinciple,SRP):一個類別應該只負責一項任務,並且這個任務應該被封裝在類別中。這樣可以提高類別的可維護性和可重複使用性。 classUser{private$id;private$name;private$email;publicfunction__construct($id,$nam
