亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

讀懂 SOLID 的「依賴倒置」原則

Snailclimb / 568人閱讀

這是理解SOLID原則中,關(guān)于依賴倒置原則如何幫助我們編寫低耦合和可測(cè)試代碼的第一篇文章。
寫在前頭

當(dāng)我們?cè)谧x書,或者在和一些別的開發(fā)者聊天的時(shí)候,可能會(huì)談及或者聽到術(shù)語SOILD。在這些討論中,一些人會(huì)提及它的重要性,以及一個(gè)理想中的系統(tǒng),應(yīng)當(dāng)包含它所包含的5條原則的特性。

我們?cè)诿看蔚墓ぷ髦?,你可能沒有那么多時(shí)間思考關(guān)于架構(gòu)這個(gè)比較大的概念,或者在有限的時(shí)間內(nèi)或督促下,你也沒有辦法實(shí)踐一些好的設(shè)計(jì)理念。

但是,這些原則存在的意義不是讓我們“跳過”它們。軟件工程師應(yīng)當(dāng)將這些原則應(yīng)用到他們的開發(fā)工作中。所以,在你每一次敲代碼的時(shí)候,如何能夠正確的將這些原則付諸于行,才是真正的問題所在。如果可以那樣的話,你的代碼會(huì)變得更優(yōu)雅。

SOLID原則是由5個(gè)基本的原則構(gòu)成的。這些概念會(huì)幫助創(chuàng)造更好(或者說更健壯)的軟件架構(gòu)。這些原則包含(SOLID是這5個(gè)原則的開頭字母組成的縮略詞):

S stands for SRP (Single responsibility principle):?jiǎn)我宦毮茉瓌t

O stands for OCP (Open closed principle):開閉原則

L stands for LSP (Liskov substitution principle):里氏替換原則

I stand for ISP ( Interface segregation principle):接口隔離原則

D stands for DIP ( Dependency inversion principle):依賴倒置原則

起初這些原則是Robert C. Martin在1990年提出的,遵循這些原則可以幫助我們更好的構(gòu)建,低耦合、高內(nèi)聚的軟件架構(gòu),同時(shí)能夠真正的對(duì)現(xiàn)實(shí)中的業(yè)務(wù)邏輯進(jìn)行恰到好處的封裝。

不過這些原則并不會(huì)使一個(gè)差勁的程序員轉(zhuǎn)變?yōu)橐粋€(gè)優(yōu)秀的程序員。這些法則取決于你如何應(yīng)用它們,如果你是很隨意的應(yīng)用它們,那等同于你并沒有使用它們一樣。

關(guān)于原則和模式的知識(shí)能夠幫助你決定在何時(shí)何地正確的使用它們。盡管這些原則僅僅是啟示性的,它們是常見問題的常規(guī)解決方案。實(shí)踐中,這些原則的正確性已經(jīng)被證實(shí)了很多次,所以它們應(yīng)當(dāng)成為一種常識(shí)。

依賴倒置原則是什么

高級(jí)模塊不應(yīng)當(dāng)依賴于低級(jí)模塊。它們都應(yīng)當(dāng)依賴于抽象。

抽象不應(yīng)當(dāng)依賴于實(shí)現(xiàn),實(shí)現(xiàn)應(yīng)當(dāng)依賴于抽象。

這兩句話的意思是什么呢?

一方面,你會(huì)抽象一些東西。在軟件工程和計(jì)算機(jī)科學(xué)中,抽象是一種關(guān)于規(guī)劃計(jì)算機(jī)系統(tǒng)中的復(fù)雜性的技術(shù)。它的工作原理一般是在一個(gè)人與系統(tǒng)交互的復(fù)雜環(huán)境中,隱藏當(dāng)前級(jí)別下的更復(fù)雜的實(shí)現(xiàn)細(xì)節(jié),同時(shí)它的范圍很廣,常常會(huì)覆蓋多個(gè)子系統(tǒng)。這樣,當(dāng)我們?cè)谂c一個(gè)以高級(jí)層面作為抽象的系統(tǒng)協(xié)作時(shí),我們僅僅需要在意,我們能做什么,而不是我們如何做。

另外,你會(huì)針對(duì)你的抽象,有一寫低級(jí)別的模塊或者具體實(shí)現(xiàn)邏輯。這些東西與抽象是相反的。它們是被用于解決某些特定問題所編寫的代碼。它們的作用域僅僅在某個(gè)單元和子系統(tǒng)中。比如,建立一個(gè)與MySQL數(shù)據(jù)庫的連接就是一個(gè)低級(jí)別的實(shí)現(xiàn)邏輯,因?yàn)樗c某個(gè)特定的技術(shù)領(lǐng)域所綁定。

現(xiàn)在仔細(xì)讀這兩句話,我們能夠得到什么暗示呢?

依賴倒置原則存在的真正意義是指,我們需要將一些對(duì)象解耦,它們的耦合關(guān)系需要達(dá)到當(dāng)一個(gè)對(duì)象依賴的對(duì)象作出改變時(shí),對(duì)象本身不需要更改任何代碼。

這樣的架構(gòu)可以實(shí)現(xiàn)一種松耦合的狀態(tài)的系統(tǒng),因?yàn)橄到y(tǒng)中所有的組件,彼此之間都了解很少或者不需要了解系統(tǒng)中其余組件的具體定義和實(shí)現(xiàn)細(xì)節(jié)。它同時(shí)實(shí)現(xiàn)了一種可測(cè)試和可替換的系統(tǒng)架構(gòu),因?yàn)樵谒神詈系南到y(tǒng)中,任何組件都可以被提供相同服務(wù)的組件所替換。

但是相反的,依賴倒置也有一些缺點(diǎn),就是你需要一個(gè)用于處理依賴倒置邏輯的容器,同時(shí),你還需要配置它。容器通常需要具備能夠在系統(tǒng)中注入服務(wù),這些服務(wù)需要具備正確的作用域和參數(shù),還應(yīng)當(dāng)被注入正確的執(zhí)行上下文中。

以提供Websocket連接服務(wù)為例子

舉個(gè)例子,我們可以在這個(gè)例子中學(xué)到更多關(guān)于依賴倒置的知識(shí),我們將使用Inversify.js作為依賴倒置的容器,通過這個(gè)依賴倒置容器,我們可以看看如何針對(duì)提供Websocket連接服務(wù)的業(yè)務(wù)場(chǎng)景,提供服務(wù)。

比如,我們有一個(gè)web服務(wù)器提供WebSockets連接服務(wù),同時(shí)客戶端想要連接服務(wù)器,同時(shí)接受更新的通知。當(dāng)前我們有若干種解決方案來提供一個(gè)WebSocket服務(wù),比如說Socket.io、Socks或者使用瀏覽器提供的關(guān)于原生的WebSocket接口。每一套解決方案,都提供不同的接口和方法供我們調(diào)用,那么問題來了,我們是否可以在一個(gè)接口中,將所有的解決方案都抽象成一個(gè)提供WebSocket連接服務(wù)的提供者?這樣,我們就可以根據(jù)我們的實(shí)際需求,使用不同的WebSocket服務(wù)提供者。

首先,我們來定義我們的接口:

export interface WebSocketConfiguration {
  uri: string;
  options?: Object;
}
export interface SocketFactory {
  createSocket(configuration: WebSocketConfiguration): any;
}

注意在接口中,我們沒有提供任何的實(shí)現(xiàn)細(xì)節(jié),因此它既是我們所擁有的抽象

接下來,如果我們想要一個(gè)提供Socket.io服務(wù)工廠:

import {Manager} from "socket.io-client";

class SocketIOFactory implements SocketFactory {
  createSocket(configuration: WebSocketConfiguration): any {
    return new Manager(configuration.uri, configuration.opts);
  }
}

這里已經(jīng)包含了一些具體的實(shí)現(xiàn)細(xì)節(jié),因此它不再是抽象,因?yàn)樗暶髁艘粋€(gè)從Socket.io庫中導(dǎo)入的Manager對(duì)象,它是我們的具體實(shí)現(xiàn)細(xì)節(jié)。

我們可以通過實(shí)現(xiàn)SocketFactory接口,來增加若干工廠類,只要我們實(shí)現(xiàn)這個(gè)接口即可。

我們?cè)谔峁┮粋€(gè)關(guān)于客戶端連接實(shí)例的抽象:

export interface SocketClient {
  connect(configuration: WebSocketConfiguration): Promise;
  close(): Promise;
  emit(event: string, ...args: any[]): Promise;
  on(event: string, fn: Function): Promise;
}

然后再提供一些實(shí)現(xiàn)細(xì)節(jié):

class WebSocketClient implements SocketClient {
  private socketFactory: SocketFactory;
  private socket: any;
  public constructor(webSocketFactory: SocketFactory) {
    this.socketFactory = webSocketFactory;
  }
  public connect(config: WebSocketConfiguration): Promise {
    if (!this.socket) {
      this.socket = this.socketFactory.createSocket(config);
    }
    return new Promise((resolve, reject) => {
      this.socket.on("connect", () => resolve());
      this.socket.on("connect_error", (error: Error) => reject(error));
    });
  }
  public emit(event: string, ...args: any[]): Promise {
    return new Promise((resolve, reject) => {
      if (!this.socket) {
        return reject("No socket connection.");
      }
      return this.socket.emit(event, args, (response: any) => {
        if (response.error) {
          return reject(response.error);
        }
        return resolve();
      });
    });
  }
  public on(event: string, fn: Function): Promise {
    return new Promise((resolve, reject) => {
      if (!this.socket) {
        return reject("No socket connection.");
      }
      this.socket.on(event, fn);
      resolve();
    });
  }
  public close(): Promise {
    return new Promise((resolve) => {
      this.socket.close(() => {
        this.socket = null;
        resolve();
      });
    });
  }
}

值得注意的是,這里我們?cè)跇?gòu)造函數(shù)中,傳入了一個(gè)類型是SocketFactory的參數(shù),這是為了滿足關(guān)于依賴倒置原則的第一條規(guī)則。對(duì)于第二條規(guī)則,我們需要一種方式來提供這個(gè)不需要了解內(nèi)部實(shí)現(xiàn)細(xì)節(jié)的、可替換的、易于配置的參數(shù)。

這也是為什么我們要使用Inversify這個(gè)庫的原因,我們來加入一些額外的代碼和注解(裝飾器):

import {injectable} from "inversify";
const webSocketFactoryType: symbol = Symbol("WebSocketFactory");
const webSocketClientType: symbol = Symbol("WebSocketClient");
let TYPES: any = {
    WebSocketFactory: webSocketFactoryType,
    WebSocketClient: webSocketClientType
};

@injectable()
class SocketIOFactory implements SocketFactory {...}
...
@injectable()
class WebSocketClient implements SocketClient {
public constructor(@inject(TYPES.WebSocketFactory) webSocketFactory: SocketFactory) {
  this.socketFactory = webSocketFactory;
}

這些注釋(裝飾器)僅僅會(huì)在代碼運(yùn)行時(shí),在如何提供這些組件實(shí)例時(shí),提供一些元數(shù)據(jù),接下來我們僅僅需要?jiǎng)?chuàng)建一個(gè)依賴倒置容器,并將所有的對(duì)象按正確的類型綁定起來,如下:

import {Container} from "inversify";
import "reflect-metadata";
import {TYPES, SocketClient, SocketFactory, SocketIOFactory, WebSocketClient} from "@web/app";
const provider = new Container({defaultScope: "Singleton"});
// Bindings
provider.bind(TYPES.WebSocketClient).to(WebSocketClient);
provider.bind(TYPES.WebSocketFactory).to(SocketIOFactory);
export default provider;

讓我們來看看我們?nèi)绾问褂梦覀兲峁┻B接服務(wù)的客戶端實(shí)例:

var socketClient = provider.get(TYPES.WebSocketClient);

當(dāng)然,使用Inversify可以提供一些更簡(jiǎn)單易用的綁定,可以通過瀏覽它的網(wǎng)站來了解。

譯者注

一般說到依賴倒置原則,往往第一個(gè)想到的術(shù)語即是依賴注入,這種在各個(gè)技術(shù)棧都有應(yīng)用,之后又會(huì)馬上想到springng等前后端框架。

我們確實(shí)是通過使用這些框架熟知這個(gè)概念的,但是如果你仔細(xì)想想的話,是否還有其他的一些場(chǎng)景也使用了類似的概念呢?

比如:

一些使用插件和中間件的框架,如express、redux

js中this的動(dòng)態(tài)綁定

js中的回調(diào)函數(shù)

也許有的人會(huì)不同意我的觀點(diǎn),會(huì)說依賴注入一般都是面向類和接口來講的,這確實(shí)有一定的道理,但是我認(rèn)為沒有必要局限在一種固定的模式中去理解依賴倒置,畢竟它是一種思想,一種模式,在js中,所有的東西都是動(dòng)態(tài)的,函數(shù)是一等公民,是對(duì)象,那么把這些與依賴倒置原則聯(lián)系起來,完全也講的通。我們真正關(guān)心的是核心問題是如何解耦,把更多的注意力投入的真正的業(yè)務(wù)邏輯中去。

歡迎關(guān)注公眾號(hào) 全棧101,只談技術(shù),不談人生

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

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

相關(guān)文章

  • 讀懂 SOLID 「里氏替換」原則

    摘要:什么是里氏替換原則某個(gè)對(duì)象實(shí)例的子類實(shí)例應(yīng)當(dāng)可以在不影響程序正確性的基礎(chǔ)上替換它們。除了在編程語言層面,在前端實(shí)際工作中,你可能會(huì)聽到一個(gè)叫作的概念,這個(gè)概念我認(rèn)為也是里氏替換原則的一直延伸。 這是理解SOLID原則,關(guān)于里氏替換原則為什么提倡我們面向抽象層編程而不是具體實(shí)現(xiàn)層,以及為什么這樣可以使代碼更具維護(hù)性和復(fù)用性。 什么是里氏替換原則 Objects should be rep...

    vibiu 評(píng)論0 收藏0
  • 讀懂 SOLID 「開閉」原則

    摘要:事件驅(qū)動(dòng)模型對(duì)于一些復(fù)雜的事件驅(qū)動(dòng)模型,比如拖拽,往往使用開閉原則會(huì)達(dá)到意想不到的效果。 這是理解SOLID原則,介紹什么是開閉原則以及它為什么能夠在對(duì)已有的軟件系統(tǒng)或者模塊提供新功能時(shí),避免不必要的更改(重復(fù)勞動(dòng))。 開閉原則是什么 Software entities (classes, modules, functions, etc.) should be open for ext...

    awkj 評(píng)論0 收藏0
  • 讀懂 SOLID 「單一職責(zé)」原則

    摘要:同時(shí),在方法命名上也投入一精力,盡可能地使方法名保持簡(jiǎn)單,它將幫助你在重構(gòu)代碼時(shí),更好的達(dá)到單一職責(zé)。 這是理解SOLID原則中,關(guān)于單一職責(zé)原則如何幫助我們編寫低耦合和高內(nèi)聚的第二篇文章。 單一職責(zé)原則是什么 之前的第一篇文章闡述了依賴倒置原則(DIP)能夠使我們編寫的代碼變得低耦合,同時(shí)具有很好的可測(cè)試性,接下來我們來簡(jiǎn)單了解下單一職責(zé)原則的基本概念: Every module o...

    Magicer 評(píng)論0 收藏0
  • 深入理解JavaScript系列10:S.O.L.I.D五大原則依賴倒置原則

    摘要:前言本章我們要講解的是五大原則語言實(shí)現(xiàn)的第篇,依賴倒置原則。當(dāng)應(yīng)用依賴倒置原則的時(shí)候,關(guān)系就反過來了。在當(dāng)靜態(tài)類型語言的上下文里討論依賴倒置原則的時(shí)候,耦合的概念包括語義和物理兩種。依賴倒置原則和依賴注入都是關(guān)注依賴,并且都是用于反轉(zhuǎn)。 前言 本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實(shí)現(xiàn)的第5篇,依賴倒置原則LSP(The Dependency Invers...

    chenjiang3 評(píng)論0 收藏0
  • Laravel深入學(xué)習(xí)12 - 依賴倒置原則

    摘要:在改變存儲(chǔ)系統(tǒng)的情況下,必須對(duì)進(jìn)行修改,違背了開放封閉原則。傳統(tǒng)的依賴痛過倒置就能事代碼變得非常靈活,易于改變 聲明:本文并非博主原創(chuàng),而是來自對(duì)《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%的原汁性,另外因?yàn)槭抢斫夥g,肯定會(huì)有錯(cuò)誤的地方,歡迎指正。 歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處,謝謝! 依賴反轉(zhuǎn)原則 ...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<