自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一篇文章帶你了解抽象泄漏(Leaky Abstractions)

開發(fā) 前端
即便文檔覆蓋相對(duì)比較全面,開發(fā)者在實(shí)現(xiàn)或排查某一些特定的問題時(shí)仍然不可避免地需要閱讀對(duì)方平臺(tái)或庫的源碼,或了解更底層的原理。

[[408479]]

本文轉(zhuǎn)載自微信公眾號(hào)「ELab團(tuán)隊(duì)」,作者ELab.xiebingyang 。轉(zhuǎn)載本文請(qǐng)聯(lián)系ELab團(tuán)隊(duì)公眾號(hào)。

在 5 月 23 日 Online Meetup With Evan You 的問答環(huán)節(jié)中,Evan 在說到 low code 時(shí)提到一個(gè)概念 —— “abstraction leak”. 在前端開發(fā)過程中接觸過很多內(nèi)部平臺(tái)和工具,包括 low code 建站平臺(tái)、組件庫、框架和二次封裝的元框架。在這個(gè)過程中會(huì)發(fā)現(xiàn)一個(gè)比較普遍的現(xiàn)象:

  • 即便文檔覆蓋相對(duì)比較全面,開發(fā)者在實(shí)現(xiàn)或排查某一些特定的問題時(shí)仍然不可避免地需要閱讀對(duì)方平臺(tái)或庫的源碼,或了解更底層的原理。
  • 一些特定的場(chǎng)景下,平臺(tái)或庫索提供的接口、界面或規(guī)范不再適用;它們的抽象層次不再能滿足業(yè)務(wù)場(chǎng)景要求。

但是往往這些痛點(diǎn)并不是系統(tǒng)本身的管理或設(shè)計(jì)缺陷;更多的是某些應(yīng)用場(chǎng)景下的“抽象泄漏”導(dǎo)致。本文翻譯多篇相關(guān)英文文章,并在此基礎(chǔ)上整合、提煉,就系統(tǒng)設(shè)計(jì)中的抽象層級(jí)和抽象泄漏現(xiàn)象進(jìn)行討論。

這篇文章將會(huì)介紹:

  • 什么是抽象泄漏法則
  • 抽象機(jī)制如何“泄漏”
  • 開發(fā)者如何應(yīng)對(duì)抽象泄漏

為了避免翻譯歧義,部分概念在特定場(chǎng)景下還會(huì)保留英文表述:

英文 翻譯
abstraction 抽象、抽象層級(jí),名詞(在某種意義上也包含“封裝”的意思)
leak 泄漏、漏洞、漏出
interface 接口(為更高一個(gè)抽象層級(jí)開發(fā)者、調(diào)用者、消費(fèi)者、使用者所展示的“界面”)
consistency 一致性;連貫、前后一致

引言

現(xiàn)在環(huán)顧四周,我們會(huì)發(fā)現(xiàn)日常生活中常常會(huì)用到一些非常復(fù)雜的系統(tǒng):智能手機(jī)、計(jì)算機(jī)、打印機(jī)、汽車、電視、烤面包機(jī)…… 雖然我們自己很難自行從零制造這樣的一個(gè)機(jī)器,但是不論這些設(shè)備或系統(tǒng)多么復(fù)雜,我們都可以正常使用它們來完成日常所需的工作。

這個(gè)小小的奇跡歸功于我們稱為 “抽象”的概念(譯者:其中也離不開 encapsulation, 即“封裝”)。抽象是一種設(shè)計(jì)概念,它簡(jiǎn)用潔的用戶界面 (interface) 屏蔽了復(fù)雜的細(xì)節(jié),使得開發(fā)者不再需要關(guān)注這些細(xì)節(jié)就可以完成工作。抽象 (abstraction) 在每個(gè)軟件程序中都起著核心的作用,這樣的設(shè)計(jì)向站在更高抽象層級(jí)的調(diào)用者和使用者隱藏或屏蔽了 API 背后的實(shí)現(xiàn)細(xì)節(jié)。但這些抽象層級(jí)常常也會(huì)發(fā)生“泄漏”。

“抽象”是什么?

用一個(gè)實(shí)際例子解釋抽象和封裝——我們可以在瀏覽器的地址欄中輸入網(wǎng)址來訪問網(wǎng)站。在大部分前端開發(fā)場(chǎng)景中我們不需要了解瀏覽器如何執(zhí)行 DNS 查找找到正確的網(wǎng)站,也不需要了解設(shè)備如何與網(wǎng)絡(luò)服務(wù)器進(jìn)行 TCP 握手,也常常不需要知道網(wǎng)站如何渲染一個(gè) DOM. 這個(gè)過程非常詳細(xì)、復(fù)雜,而很慶幸瀏覽器底層的邏輯幫我們完成了這些操作,我們不需要實(shí)現(xiàn)這些能力,在大部分場(chǎng)景中也不需要關(guān)心這些實(shí)現(xiàn)。

在計(jì)算機(jī)軟件設(shè)計(jì)中,隨著軟件本身的迭代、軟件系統(tǒng)體積和復(fù)雜度增加,我們會(huì)不斷構(gòu)建新的抽象層級(jí),并將其添加到已有的抽象層級(jí)中、豐富已有的抽象和封裝。做任何設(shè)計(jì)都是思考如何創(chuàng)建正確合理的抽象層級(jí)的過程。一個(gè)設(shè)計(jì)合理的抽象層次會(huì)向上層暴露所有重要的和必要的實(shí)現(xiàn),但同時(shí)隱藏所有不必要的細(xì)節(jié)。一個(gè)合理的抽象層次會(huì)掌握好控制度與復(fù)雜度之間的平衡。一個(gè)合理的抽象層次可以輕松把它調(diào)用者的行為或執(zhí)行任務(wù)映射到自身方法或?qū)傩陨稀H绻橄髮哟卧O(shè)計(jì)得當(dāng),它會(huì)讓人感覺使用它很直接、便利,合乎常規(guī)邏輯。

To design something — anything — is to think about creating the right abstraction.

在軟件工程領(lǐng)域中,對(duì)抽象層級(jí)設(shè)計(jì)的關(guān)注會(huì)更加突出。在編寫任何的代碼時(shí)都需要考慮易用性和可維護(hù)性,一個(gè)開發(fā)者需要思考如何向其他代碼隱藏這部分的內(nèi)部原理,又需要思考如何讓使用者順利地消費(fèi)這段代碼的功能。抽象設(shè)計(jì)這個(gè)龐大的工程中至關(guān)重要的環(huán)節(jié)包括我們耳熟能詳?shù)脑O(shè)計(jì)模式、命名、單元測(cè)試等等,這些看似關(guān)聯(lián)不密切的關(guān)注點(diǎn)都有一個(gè)共同的目標(biāo)——在開發(fā)者設(shè)計(jì)抽象層時(shí),幫助我們做出正確的決策,并保持其效果可控。

因?yàn)橛?ldquo;抽象” (abstraction) 設(shè)計(jì)的存在,我們可以在 HTML 文檔中直接編寫 <button> 而不需要繪制單個(gè)像素。我們可以編寫 SQL 查詢來獲取客戶的訂單歷史記錄,但不需要知道每一條記錄存儲(chǔ)的位置和大小。我們可以在不了解打印機(jī)語言的情況下打印文件,在不了解視頻編解碼器的情況下播放視頻文件,在不手動(dòng)從硬盤上一個(gè)群集跳轉(zhuǎn)到另一個(gè)群集的情況下讀取文本文件,在不管理內(nèi)存地址的情況下存儲(chǔ)數(shù)據(jù)集合。(當(dāng)然,如果真的想這樣做,也是可以的。)

抽象如何“泄漏”

有一個(gè)真理:所有非簡(jiǎn)單抽象層級(jí)都會(huì)泄漏。這個(gè)原則是由 Stack Overflow 聯(lián)合創(chuàng)作者 Joel Spolsky 在 2002 年提出的,在國(guó)內(nèi)文獻(xiàn)中,有些人也將其翻譯為“抽象漏洞”、“技術(shù)露底”。它的含義是:任何試圖減少或隱藏復(fù)雜性的抽象,其實(shí)都并不能完全屏蔽細(xì)節(jié);試圖被隱藏的復(fù)雜細(xì)節(jié)總是可能會(huì)從抽象層級(jí)中“泄漏”出來。

以下圖中黑色實(shí)線可以理解為“已定義的復(fù)雜度”;紅色實(shí)現(xiàn)為“超出定義范圍、超出預(yù)期的復(fù)雜度”:

圖片來源:https://javadevguy.wordpress.com/2017/11/02/transcending-the-limitations-of-the-human-mind/

一種定義是:在軟件中,假設(shè)第 n 層抽象與第 n+1 層抽象和第 n-1 層抽象交互。第 n 層的實(shí)現(xiàn)復(fù)雜度為 N(n), 且它向第 n-1 層提供了范圍為 A(n, n-1) 的 API. 當(dāng)?shù)?n-1 層需要了解 N(n) - A(n, n-1) 的部分以實(shí)現(xiàn)某些功能,則發(fā)生了抽象泄露。

另一種定義是:在軟件中,如果第第 n 層抽象與第 n+1 和 n-1 層交互,但是第 n-1 層應(yīng)該保證第 n 層不需要知道第 n-2 層的細(xì)節(jié)。如果第 n-2 層的實(shí)現(xiàn)細(xì)節(jié)出于某種原因暴露至第 n 層的細(xì)節(jié),則發(fā)生了抽象泄漏。

通過建立抽象,我們可以在更高的層次上思考和編程。

抽象泄漏定律意味著:軟件市場(chǎng)上出現(xiàn)一個(gè)有趣的新工具,而且這個(gè)工具聲稱可以如何如何提高我們的工作效率時(shí),更資深一些的開發(fā)者會(huì)說:“你先要學(xué)習(xí)怎么手動(dòng)操作,然后再使用這些新工具來節(jié)省時(shí)間。” 在學(xué) Vue 和 React 的 VDOM 之前我們需要先了解什么是實(shí)體 DOM. 新的編程工具實(shí)際上創(chuàng)造了一個(gè)抽象層級(jí),它抽象出某種東西,而這個(gè)抽象層級(jí)如同其他所有抽象一樣在實(shí)際使用場(chǎng)景中總難免需要開發(fā)者在一定程度上了解它們的細(xì)節(jié)和實(shí)現(xiàn)原理:舉最簡(jiǎn)單的例子,我們需要了解 Vue 雙向綁定的機(jī)制,以避免出現(xiàn)響應(yīng)式對(duì)象無法更新的情況,這就是一個(gè)抽象泄漏。有效處理這些“泄漏” (leak) 的唯一方法是了解這個(gè)抽象層級(jí)的工作方式、了解它們到底向我們屏蔽了什么內(nèi)容。抽象層級(jí)節(jié)省了我們的工作時(shí)間,但并沒有節(jié)省我們學(xué)習(xí)的時(shí)間。這也意味隨著技術(shù)的發(fā)展,我們擁有越來越高級(jí)的編程工具,建立越來越好的抽象、模型、設(shè)計(jì)理念,但精通編程這件事可能反而變得越來越困難。

但是這個(gè)規(guī)則為什么會(huì)存在?我們?yōu)槭裁床荒芙⑼昝赖某橄髮蛹?jí)?

這個(gè)問題在于,雖然抽象存在的意義是為了屏蔽細(xì)節(jié),但抽象 (abstraction) 的價(jià)值也正是在于它所屏蔽或隱藏的細(xì)節(jié)當(dāng)中。一個(gè)好的抽象應(yīng)該做減法,也就需要將一些細(xì)節(jié)隱藏在調(diào)用者視線之外。但原 API 的設(shè)計(jì)范疇是有限的,其復(fù)雜度和操作支持范圍必定是它更下層抽象的一個(gè)子集。

The value of an abstraction is in the details that it hides.

在一個(gè)抽象層級(jí)“泄漏”得過于頻繁或泄漏規(guī)模過大時(shí),導(dǎo)致開發(fā)者需要真正了解所有本應(yīng)該被隱藏的細(xì)節(jié),抽象層級(jí)實(shí)際上就已經(jīng)失效了。這個(gè)所謂“便利”的抽象層級(jí)其實(shí)就沒有節(jié)省開發(fā)者任何時(shí)間或精力。軟件設(shè)計(jì)中的真正藝術(shù)是如何正確識(shí)別抽象層級(jí)、學(xué)會(huì)處理這些 abstractions 的漏洞,學(xué)會(huì)什么時(shí)候、以什么方式補(bǔ)全這些抽象層級(jí)上的缺口。

分割線內(nèi)為譯者注解

為什么會(huì)出現(xiàn)抽象泄漏 (abstraction leak)?簡(jiǎn)單說可能有幾個(gè)原因:

  • 接口 (interface) 暴露的細(xì)節(jié)太多
  • 接口 (interface) 所屏蔽的細(xì)節(jié)太多
  • 抽象層級(jí)設(shè)計(jì)缺乏一致性 (consistency)
  • 抽象層級(jí)缺乏完好的注解

接口暴露細(xì)節(jié)太多

 例1:

Low code 或 no code 平臺(tái)是抽象泄漏的典型。部分 Low code 建站平臺(tái)的一個(gè)重要目標(biāo)是賦能產(chǎn)品運(yùn)營(yíng)或非技術(shù)人員,但 low code 平臺(tái)在設(shè)計(jì)時(shí)往往無法完全屏蔽技術(shù)。一部分類似平臺(tái)會(huì)提供自定義代碼的功能,在實(shí)現(xiàn)產(chǎn)品賦能的同時(shí)滿足一定的研發(fā)靈活度。另一些 low code 平臺(tái)會(huì)屏蔽這些抽象層,取消頁面內(nèi)代碼編輯器的支持。(提出這種平臺(tái)有抽象泄漏的存在只是陳述事實(shí)、不是帶有任何價(jià)值觀色彩;辯證地看,有些抽象泄露不一定是一個(gè)壞事。)

某個(gè) low code 平臺(tái)初期會(huì)提供幾乎所有可以映射到 CSS 的屬性功能,但在后續(xù)的迭代版本中去除了這些屬性,只保留最簡(jiǎn)單必要的“位置”、“背景圖”等屬性,引導(dǎo)使用者將自身的需要功能往已有屬性上映射。如文字需求映射為圖片、動(dòng)畫需求映射為 gif 等,大大的降低了業(yè)務(wù)人員對(duì)平臺(tái)功能的認(rèn)知成本。

例2:

這一個(gè)設(shè)計(jì)的優(yōu)化空間非常大——但是設(shè)計(jì)出來一個(gè)直接后果往往是因?yàn)楸┞短鄡?nèi)容,導(dǎo)致優(yōu)化成本過高。

實(shí)際上優(yōu)化方式很多:使用狀態(tài)管理,父組件不需要處理這些參數(shù),在子組件內(nèi)直接處理數(shù)據(jù);定義幾個(gè)合理的模型:shareConfig, activityConfig, 將數(shù)據(jù)在模型層封裝;放棄父組件的數(shù)據(jù)抽象層、子組件按需加載數(shù)據(jù)(GraphQL 是一種方案);…… 在一定程度都涉及到了抽象邊界的劃分、兩個(gè)抽象層級(jí)之間 API 的重新定義和設(shè)計(jì)。

接口屏蔽的細(xì)節(jié)太多

細(xì)節(jié)屏蔽的太多,接口范圍太窄,導(dǎo)致開發(fā)者真正想實(shí)現(xiàn)一些其他功能時(shí)只能去關(guān)注內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)。

例1:

前端框架和庫可能是一個(gè)很好的例子。React 16 及之前的事件封裝導(dǎo)致使用原生方法掛在 DOM 事件可能產(chǎn)生不可預(yù)期的后果;一個(gè)組件庫可能會(huì)重寫原生方法,如 Input 組件只暴露 onChange 和 onFocus 這類屬性,導(dǎo)致無法支持像 onCompositionStart 等小眾但真實(shí)存在的需求。

例2:

初學(xué)前端封裝某些組件時(shí),可能會(huì)傾向于只封裝自己用到的部分:

一旦業(yè)務(wù)需求有變動(dòng)、要求樣式變化,如果不重構(gòu)已有的這個(gè)組件,就只能在它的調(diào)用方里關(guān)注這個(gè)組件實(shí)現(xiàn)的細(xì)節(jié)(即樣式和結(jié)構(gòu))在其父組件里覆蓋樣式,造成了抽象泄漏。但一個(gè)更好的設(shè)計(jì)可能是如圖2:在提供了一定的規(guī)范的基礎(chǔ)上,向調(diào)用方提供足夠靈活的 API、暴露可控的復(fù)雜度。

抽象層級(jí)設(shè)計(jì)缺乏一致性

當(dāng)調(diào)用者或使用者難以理解抽象層級(jí)所提供的接口時(shí),和業(yè)界規(guī)范不符合、組件或庫內(nèi)部方法調(diào)用不一致會(huì)造成抽象泄漏現(xiàn)象——消費(fèi)者或調(diào)用者(consumer)需要去了解或確認(rèn)內(nèi)部的實(shí)現(xiàn)。這個(gè)是命名在抽象層級(jí)設(shè)計(jì)中重要性的一個(gè)例子。

但更多的不一致性不是命名這么簡(jiǎn)單,可能是這個(gè)抽象 (abstraction) 內(nèi)部本身設(shè)計(jì)的不一致、設(shè)計(jì)缺陷導(dǎo)致的。比如服務(wù)內(nèi)部不同函數(shù)返回是否緩存、緩存機(jī)制不一致;再比如后臺(tái)訪問一個(gè)學(xué)生的某些信息,A 接口需要傳入 parentId, B 接口需要 studentId;這個(gè)時(shí)候調(diào)用方就需要了解這兩個(gè) id 之間的映射關(guān)系、甚至相互獲取的邏輯。

抽象層級(jí)缺乏完好的注解

除了常規(guī)定義的“缺乏注釋”以外,缺少注解還可以表現(xiàn)為:缺乏類型定義和功能定義,調(diào)用者從抽象層級(jí)外觀察難以低成本理解功能(接收什么參數(shù)、返回什么內(nèi)容、在什么時(shí)機(jī)觸發(fā));因?yàn)樵O(shè)計(jì)本身導(dǎo)致在調(diào)用層級(jí)中無法輕松看到或理解所調(diào)用的 API. 有些開發(fā)者認(rèn)為最好的代碼是自注釋的代碼。

在 React hooks 之前,代碼復(fù)用通常會(huì)使用 mixin 或 HOC (高級(jí)組件). 在使用 mixin vs. HOC 的爭(zhēng)論中 React 官方是更推薦 HOC 的[1]:mixin 會(huì)引入大量不可控因素,引入隱形依賴、潛在命名沖突、復(fù)雜度急劇增加。

Composition over inheritance.

在 hooks 之后,設(shè)計(jì)模式的變動(dòng)和編程范式轉(zhuǎn)移,使得代碼共享就更加簡(jiǎn)單、直接,代碼在一定程度上可以更加“自動(dòng)注釋”化 (self-documenting).

在一定程度上,“缺乏一致性”和“缺乏完好的注解”是抽象層級(jí)本身設(shè)計(jì)的一個(gè)缺陷。

”暴露的信息太多“、“屏蔽的信息太多”有一部分是設(shè)計(jì)缺陷;有一部分更可能是使用的場(chǎng)景或面向的受眾 (consumer) 已經(jīng)偏離抽象層級(jí)設(shè)計(jì)的初衷,抽象層級(jí)在這一個(gè)場(chǎng)景內(nèi)或面對(duì)這樣的調(diào)用者時(shí)已經(jīng)不再完全適用。

開發(fā)者如何處理上游的抽象泄漏?

抽象和封裝降低了系統(tǒng)復(fù)雜度,但它們不是完美的解決方案。如果抽象泄漏太嚴(yán)重,我們可以直接刪除這個(gè)抽象層級(jí),或創(chuàng)造一個(gè)更好的抽象。我們可以以文檔和注釋形式清清楚楚地記錄下它的功能和局限性。抽象和封裝是好事,但過多的抽象也反而會(huì)增加系統(tǒng)的復(fù)雜度。David J. Wheeler 指出:“計(jì)算機(jī)科學(xué)中的所有問題——除了“中間層太多”這個(gè)問題以外——都可以通過增加一個(gè)中間層解決。”

All problems in computer science can be solved by another level of indirection, except for the problem of too many layers of indirection.

--David J. Wheeler

增加一個(gè)抽象層

開發(fā)人員可以在這個(gè)抽象層級(jí)的基礎(chǔ)上二次封裝,增加一個(gè)抽象層級(jí),達(dá)到屏蔽一些信息的目的。下游應(yīng)用層會(huì)改調(diào)用這個(gè)新的抽象層級(jí),這個(gè)抽象層級(jí)也會(huì)在它的層面收斂邏輯來完成下游應(yīng)用所期望的行為。中臺(tái)是一個(gè)典型的例子:業(yè)務(wù)發(fā)展到一定規(guī)模,復(fù)雜度變高,原有的抽象層級(jí)泄漏嚴(yán)重不能滿足需求,所以抽象出一個(gè)新的中間層去統(tǒng)一處理邏輯、向調(diào)用方屏蔽實(shí)現(xiàn)細(xì)節(jié)。字節(jié) Web Infra 一部分團(tuán)隊(duì)成員也認(rèn)為:長(zhǎng)久以來 UX 和 DX 之間的矛盾是因?yàn)殚_發(fā)者所面臨的抽象層級(jí)過低;如果做更高一級(jí)的基礎(chǔ)建設(shè),可以實(shí)現(xiàn)二者的雙贏——這就是企業(yè)內(nèi)部元框架的誕生了。

重寫或拋棄抽象層

在更極端的情況下,開發(fā)者可以重新實(shí)現(xiàn)功能,甚至拋棄原有的抽象層次。這不是一個(gè)好習(xí)慣;隨著抽象層次的丟失,應(yīng)用程序復(fù)雜度會(huì)提高。拋棄抽象層次的選擇是在兩種復(fù)雜度之間進(jìn)行權(quán)衡取舍:取因?yàn)槌橄笕笔淼捻?xiàng)目復(fù)雜度,還是取抽象泄漏的復(fù)雜度。

如果重新實(shí)現(xiàn)的抽象層級(jí)不能像原抽象層級(jí)那么優(yōu)雅、達(dá)到原抽象 (abstraction) 的可用度,重新實(shí)現(xiàn)基礎(chǔ)功能還可能導(dǎo)致應(yīng)用程序的其余部分(僅使用重新實(shí)現(xiàn)的功能的代碼部分)變得更加復(fù)雜。開發(fā)者由于某種原因無法在新的接口 (interface) 下兼容舊的接口 (interface) 時(shí),也容易出現(xiàn)這個(gè)問題。如果業(yè)務(wù)程序員被迫放棄舊的接口、轉(zhuǎn)而開始自己思考軟件設(shè)計(jì),這些開發(fā)者的產(chǎn)出其實(shí)很可能達(dá)不到原抽象層級(jí)的水準(zhǔn)??陀^、不帶有價(jià)值判斷地說,業(yè)務(wù)開發(fā)者當(dāng)前的第一優(yōu)先級(jí)仍然是業(yè)務(wù),業(yè)務(wù)開發(fā)者中大部分人可能沒有時(shí)間(或沒有興趣)去真正設(shè)計(jì)清晰優(yōu)雅的系統(tǒng)。同樣,重寫抽象層也是一種權(quán)衡取舍:開發(fā)者接受了新引入的潛在抽象泄漏;放棄了“現(xiàn)在”的抽象泄漏、接受了“未來”的抽象泄漏。至于如何取舍也需要辯證地、根據(jù)實(shí)際情況判斷。

繞過抽象泄漏

另一種方法是所謂 "code between the lines":在了解抽象泄漏的基礎(chǔ)上,繞過它,或?qū)iT“為了它”編寫代碼。開發(fā)者需要了解抽象背后的實(shí)現(xiàn)細(xì)節(jié),強(qiáng)扭業(yè)務(wù)代碼來適配抽象層級(jí)的實(shí)現(xiàn)。這會(huì)使得代碼復(fù)雜度增加,可讀性和移植性也會(huì)變差。

Coding between the lines 的一個(gè)典型的例子是虛擬內(nèi)存。一個(gè)程序給多個(gè)對(duì)象分配內(nèi)存時(shí),通常會(huì)有一個(gè)“自然”的分配順序。但如果對(duì)象很多、內(nèi)存分頁行為變得很關(guān)鍵時(shí),人們通常會(huì)重寫程序讓“對(duì)象內(nèi)存分配得靠近一些”,從而提升程序的性能。盡管虛擬內(nèi)存著一層抽象相關(guān)的文檔沒有提及對(duì)象存儲(chǔ)的物理位置,但是程序員設(shè)法“扭曲”了自己的代碼,讓自己的代碼直接和抽象層級(jí)的內(nèi)部實(shí)現(xiàn)一個(gè)“對(duì)話”,來獲得所需的性能提升。顯而易見當(dāng)程序員被迫這樣編碼時(shí),他們的程序復(fù)雜度將會(huì)顯著提高——而且更重要的是,這樣的代碼可移植性會(huì)降低。

一般而言開發(fā)者最開始的代碼實(shí)現(xiàn)會(huì)更簡(jiǎn)單、清晰、直接,并且最大程度地復(fù)用了底層的抽象,這個(gè)時(shí)候開發(fā)者是面向一個(gè)最理想狀態(tài)(理想內(nèi)存、理想 CPU、理想網(wǎng)絡(luò)、理想數(shù)據(jù)等等)進(jìn)行編程的。但當(dāng)程序需要實(shí)際交付共時(shí)可能會(huì)出現(xiàn)一些實(shí)際和底層抽象綁定或耦合問題:如何適配不同機(jī)器?如何利用交付環(huán)境提高程序的性能?……圍繞抽象泄漏編碼就相當(dāng)于引進(jìn)來一個(gè)魔法師,這個(gè)魔法師實(shí)際上運(yùn)用他對(duì)內(nèi)部工作原理的知識(shí),將已有的簡(jiǎn)單的代碼實(shí)現(xiàn)和涉及到的抽象層級(jí)背后的原理相結(jié)合(結(jié)合,原文為 convolve, 直譯為“卷積”,即扭曲在一起)去“神奇地”實(shí)現(xiàn)這個(gè)功能,也就是我們常常說的 "hack 一下"。原本的代碼可以實(shí)現(xiàn)局部的控制,將復(fù)雜度限定在某一個(gè)范圍內(nèi),但在這樣為了抽象泄漏專門編碼會(huì)將代碼打散、外露復(fù)雜度、外露細(xì)節(jié)實(shí)現(xiàn)。這個(gè)過程中,代碼也隱含地和交付平臺(tái)或所依賴的底層抽象更加耦合;耦合度提高也意味著可移植性降低。

開發(fā)者如何設(shè)計(jì)抽象層?

抽象層級(jí)的設(shè)計(jì)是很復(fù)雜的。但是我們可以先列舉出四個(gè)相互聯(lián)系的初步設(shè)計(jì)原則:范圍控制、概念分離、增量性、健壯性。

  • 【范圍控制】指抽象層級(jí)應(yīng)在一定的范圍內(nèi)給予調(diào)用者適當(dāng)?shù)目刂茩?quán)。范圍控制有很多種。幾個(gè)更貼切和實(shí)際的例子可能是 low code 平臺(tái)對(duì)定義好的配置項(xiàng)有充分的控制;前端框架對(duì)函數(shù)中拋出異常的處理;組件庫組件在封裝一些基本能力以外都會(huì)提供 className 或類似屬性支持調(diào)用者在一定程度上覆蓋樣式。
  • 【概念分離】意味著使用者或調(diào)用者應(yīng)當(dāng)不需要了解整個(gè)抽象層級(jí)的實(shí)現(xiàn)原理,就可以使用抽象層的接口實(shí)現(xiàn)某些特定的功能。在系統(tǒng)設(shè)計(jì)層面上這是很困難的,因?yàn)榫唧w的實(shí)現(xiàn)有時(shí)不同的變量、方法、屬性之間的交互可能會(huì)產(chǎn)生令人意想不到的深遠(yuǎn)的影響。
  • 【增量原則】意味著如果一個(gè)開發(fā)者決定自定義這個(gè)抽象層的某一個(gè)部分,調(diào)用方應(yīng)當(dāng)可以聲明式地改變他們想自定義的內(nèi)容,然后完全復(fù)用抽象層其他的部分。一個(gè)開發(fā)者不應(yīng)該為了部分的自定義實(shí)現(xiàn)承擔(dān)全部抽象層級(jí)范圍的責(zé)任,他們也不應(yīng)該需要從零開始重新編寫一個(gè)新的實(shí)現(xiàn)方式。
  • 【健壯性】意味著客戶程序中的錯(cuò)誤的影響應(yīng)受到適當(dāng)?shù)南拗?,一部分的錯(cuò)誤對(duì)系統(tǒng)其余部分的影響可控。

抽象泄漏在所難免;除此之外,針對(duì)抽象層級(jí)如何泄露的具體方法有:

  • 確保所提供的抽象層級(jí)具有一致性 (consistency). 確保在這一個(gè)層級(jí)中提供統(tǒng)一的、一致的、同一個(gè)認(rèn)知層面的抽象。
  • 明確抽象層級(jí)的適用范圍。告知開發(fā)者或用戶這個(gè)抽象層級(jí)的明確的適用范圍、應(yīng)用背景,在超出適用范圍后哪里可能存在抽象泄漏、他們可以從什么角度處理。
  • 引入輔助或并行的抽象,讓調(diào)用方有更多選擇。如提供簡(jiǎn)單模式、復(fù)雜模式;用戶不認(rèn)為多模式這件事本身是抽象泄漏。同上條一樣,管理抽象、防止抽象泄漏在很大程度上是管理用戶的期望和認(rèn)知。
  • 擁抱抽象泄漏,并把它當(dāng)作抽象層級(jí)的一部分,在制定好規(guī)范的基礎(chǔ)上鼓勵(lì)調(diào)用者填補(bǔ)框架的認(rèn)知空白。典型例子如 webpack, eslint 等工具的插件機(jī)制。與其逼迫調(diào)用者 "hack" 你的抽象層,不如提供一個(gè)入口、邀請(qǐng)他們共建。

Exposing an abstraction leak might be the most effective solution to hide it

總結(jié) TL;DR

  • 計(jì)算機(jī)領(lǐng)域各處存在抽象和封裝。設(shè)計(jì)任何東西都是思考如何創(chuàng)建正確的抽象層級(jí)的一個(gè)過程。
  • 任何試圖減少或隱藏復(fù)雜性的抽象其實(shí)并不能完全屏蔽實(shí)現(xiàn)細(xì)節(jié);試圖被隱藏的復(fù)雜細(xì)節(jié)總是可能會(huì)從抽象層級(jí)中“泄漏”出來。
  • 抽象泄漏的幾個(gè)直接表現(xiàn)可能是:
    • 暴露細(xì)節(jié)太多
    • 暴露細(xì)節(jié)太少
    • 設(shè)計(jì)缺乏一致性
    • 缺乏完好的注解
  • 針對(duì)上游服務(wù)抽象泄漏,開發(fā)者可以:
    • 增加一個(gè)抽象層
    • 重寫或拋棄上游抽象層
    • 繞過抽象漏洞去編碼、或“針對(duì)”抽象漏洞去編碼
  • 開發(fā)者設(shè)計(jì)抽象層時(shí),需要注意:
    • 抽象層級(jí)需要在一定的范圍內(nèi)給予下游適當(dāng)?shù)目刂茩?quán)
    • 抽象層級(jí)之間概念互相分離(高內(nèi)聚、低耦合)
    • 支持讓調(diào)用方聲明式地改變他們想自定義的內(nèi)容
    • 客戶程序中的錯(cuò)誤對(duì)系統(tǒng)其余部分的影響需要可控、有限
  • 更具體一些,針對(duì)已有的抽象漏洞,可以:
    • 確保所提供的接口 (interface) 的一致性
    • 明確抽象層級(jí)的適用范圍
    • 引入一個(gè)輔助或并行的抽象
    • 擁抱抽象泄漏,并把它當(dāng)作抽象層級(jí)的一部分

參考文獻(xiàn)

除譯者注部分和中間穿插少部分舉例,其他均翻譯+整合自:

  • What are Leaky Abstractions - an Illustrated Guide[2]
  • The Law of Leaky Abstractions[3]
  • Leaky Abstractions[4]
  • Plugging Leaky Abstractions[5]
  • Towards a New Model of Abstraction in the Engineering of Software[6]

參考資料

[1]React 官方是更推薦 HOC 的: https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html

[2]What are Leaky Abstractions - an Illustrated Guide: https://medium.com/young-coder/what-are-leaky-abstractions-an-illustrated-guide-f2982ff21cae

[3]The Law of Leaky Abstractions: https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/

[4]Leaky Abstractions: https://alexkondov.com/leaky-abstractions/

[5]Plugging Leaky Abstractions: https://blog.ndepend.com/plugging-leaky-abstractions/

[6]Towards a New Model of Abstraction in the Engineering of Software: http://www.itu.dk/people/ydi/PhD_courses/adaptability_design/kiczales92towards.pdf

 

責(zé)任編輯:武曉燕 來源: ELab團(tuán)隊(duì)
相關(guān)推薦

2025-02-14 09:53:50

2023-05-12 08:19:12

Netty程序框架

2021-06-30 00:20:12

Hangfire.NET平臺(tái)

2023-07-30 15:18:54

JavaScript屬性

2023-05-08 08:21:15

JavaNIO編程

2021-01-26 23:46:32

JavaScript數(shù)據(jù)結(jié)構(gòu)前端

2021-03-09 14:04:01

JavaScriptCookie數(shù)據(jù)

2021-06-24 09:05:08

JavaScript日期前端

2021-09-27 09:18:30

ListIterato接口方法

2023-09-06 14:57:46

JavaScript編程語言

2024-01-30 13:47:45

2024-04-19 14:23:52

SwitchJavaScript開發(fā)

2020-12-08 08:09:49

SVG圖標(biāo)Web

2021-03-05 18:04:15

JavaScript循環(huán)代碼

2021-05-18 08:30:42

JavaScript 前端JavaScript時(shí)

2021-02-26 20:01:57

SVG濾鏡元素

2020-11-10 10:48:10

JavaScript屬性對(duì)象

2021-01-29 18:41:16

JavaScript函數(shù)語法

2021-02-02 18:39:05

JavaScript

2021-06-04 09:56:01

JavaScript 前端switch
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)