為什麼你需要知道介面的基本原理

簡評:作者從編程語言和軟體工程兩個角度闡釋了介面的作用。在編程語言中,介面聲明了一個特定的規則,用來控制對象之間的交互;在軟體工程中,介面為兩個不能直接連接的系統(或其中的部分)提供了連接作用。

語音版本請點擊此處。

在 Java 中,你可以使用 interface 關鍵字創建一個介面。

介面類型有許多規則,其中一條是只要一個類聲明 implements 這個介面,就會被強制實現其中聲明的方法:

interface Vehicle {n void start();n}nnclass Car implements Vehicle {n public void start() {n // Omittedn }n}nnnew Car().start();n

如果 Car 沒有實現 Vehicle 介面聲明的 start() 方法,就會出現一個編譯錯誤:

interface Vehicle {n void start();n}nn// error: Car is not abstract and does not override abstractn// method start() in Vehiclen//tclass Car implements Vehicle {n//t^nclass Car implements Vehicle {n // Omittedn}n

在像 Java 的編程語言中,「介面」是用來聲明一個類型的關鍵字,它包含了一些特定的規則,用於控制環境中對象之間的交互。

然而,在軟體工程中談及介面,它們可能比編程語言中的關鍵字概念更為深刻。

在軟體工程中,一個「介面」表示兩個不能直接連接的系統(或其中的部分)的連接部分。

介面可以有更為概念和廣泛的定義,取決於上下文。

讓我們來想像一下你是設計城市的主管人。假設你需要創造很多類型的馬路和標誌。有泥路(因為投資回報率不夠大,不用太好),後方道路(住宅區寬度較窄)和高速公路(為避免堵車)。 有交通信號燈(組織流量),速度標誌(通知安全速度)和交通線路(以防意外碰撞)。

你可以說這條路給一輛小車,卡車或者或者從一個安全點轉移到另一個安全點。標誌能夠讓他們知道要去哪裡。

你也可以說城市是一個環境,道路是一個介面,包含了一個公共特徵:它允許車輛(小車,卡車,或貨車)在上面行駛,並且可以添加標誌(交通燈,速度標誌和交通線路)。

每種類型的路(泥路,後方道路或高速公路)將實現不同類型的標誌。一些高速公路不需要交通燈但需要速度標誌和線路指示。一些泥路不需要交通線但需要交通燈和速度標誌。

一條路是一個介面,接受一種交通工具(同樣是個介面),並且能讓交通工具在上面行駛。

每個國家都會有它自己的道路實現。在澳大利亞,你將會在左側道路行駛,還有不同的標誌比如箭頭交通燈。在巴西,你將會在右側道路行駛,但沒有箭頭交通燈。

許多供應商和國家會以不同的方式實現交通工具,但是有通用介面標準:

  • 都有輪子;
  • 能適應道路;
  • 都有車窗,以便司機看到標誌。

如果車輛沒有正確地實現標準介面——如果說一輛車可以飛或者不能開——那麼它將不能在公共交通中有效運行。這就是為什麼當我們說到環境中一般類型的連接時,介面(道路和交通工具)在架構中的交流那麼有用的原因,而不是使用具體的術語(澳大利亞的泥路,或者巴西的卡車和貨車)。

一個介面可以有效地歸納屬性。它提供通訊能供性,而不用考慮有多少種介面類型的實現。

一個真實的生活對象或者環境提供另一個對象的服務都可以聲明為某種形式的「介面」。

計算機的視頻圖形陣列(V.G.A.)的連接頭同樣也是個介面。它可以接通不同類型的屏幕。當暴露了 「V.G.A.」 公共連接方式後,任何屏幕生產商都能生產無縫連接這種介面的產品。

甚至不需要一個計算機!

介面代表了能力的概念,能夠將某些東西與特定的特徵連接起來。其他的任何東西都可以不同,只要連接方式符合相同的標準格式。

在前端中也通用。

想像一下,你必須使用組件模式創建包含電影描述的一組標籤:

<tabs-container>n <tab id="movie-summary">Movie Summary</tab>n <tab id="movie-cast">Movie Cast</tab>n</tabs-container>n<div id="movie-summary">n <!-- The movie summary content -->n <!-- This is irrelevant for the purpose of this exercise -->n</div>n<div id="movie-cast">n <!-- The movie cast content -->n <!-- This is also irrelevant for the purpose of this exercise -->n</div>n

標籤容器組件將提供一個註冊選項卡。它還將在初始化時激活第一個註冊的選項卡,以便讓用戶沒有點擊任何東西時可以看到一些內容:

const TabsContainerController = () => {n const registeredTabs = [];nn return {n // register(tab) affords a tab to be registered on TabsContainerControllern register: (tab) => {n registeredTabs.push(tab);n },nn // When the TabsContainerController initializes,n // it will activate the first registeredTabn onInit: () => {n registeredTabs.forEach((tab, index) => {n if (index === 0) {n // TabsContainerController is the client calling the .activate()n tab.activate();n }n });n }n }n}n

每一個標籤初始化時,都將在標籤容器中註冊。它還將提供客戶端(這個例子中是標籤容器)來激活它:

const TabController = () => {n return {n // Each tab registering itself in the tabs containern onInit: () => {n const currentTabController = this;n tabsContainer.register(currentTabController);n },nn // activate() affords TabsContainerController to call it and activate this tabn activate: () => {n const currentTabController = this;n currentTabController.active = true;n }n }n};n

注意到下面這行中 TabController 使用 「current tab controller」 作為參數調用了 register 函數:

tabsController.register(currentTabController);n

在 register 函數中,接收到參數名為 「tab」:

register: (tab) => {n registeredTabs.push(tab);n},n

你可以說標籤容器提供註冊標籤。但是,標籤正在註冊它自己的控制器,而不是新的「標籤」對象實例。通過閱讀源代碼,可以知道 tab controller 實現了 tab 介面。即使沒有在任何地方明確地聲明 interface 或者 new Tab() 實例,參數的名字和它被 TabsContainerController 使用的方式表明了參數代表著什麼。

用 「tab controller」 來命名參數是不合理的:

register: (tabController) => {n registeredTabs.push(tabController);n},n

標籤容器不需要知道傳過來的是不是一個控制器。同樣,它不應該使用任何控制器聲明的函數,並且與標籤無關。

它只需要知道 3 件事:

1. 像被傳過來的標籤的「東西」作為參數:

register: (tab) => {n

2. 這個「東西」可以被存儲在一個註冊的標籤數組裡:

if (index === 0) {n tab.activate();n}n

3. 這個「東西」有一個 activate() 函數:

if (index === 0) {n tab.activate();n}n

這些是 TabsContainerController 所需要從 「tab」 中知道的所有事情。

如果它像一個標籤那樣註冊和激活,那麼它一定是個標籤,即使它是個控制器。

或者換句話說:

如果它像鴨子一樣走路,像鴨子一樣叫,那它一定是只鴨子

—— Duck Tying on Wikipedia

在 JavaScript 中,interface 關鍵字是存在的,但是它是個保留關鍵字,不做任何事情。Duck typing 是一個讓你的代碼結構化的解決方案,只要你理解介面的真正含義。

我以前說過理解語法或者工具是不夠的。為了寫出好又多的軟體,你需要理解一個概念的基本原理。

對於介面,也是一樣的。

編程遠遠超出了解決謎題和編寫難以理解的代碼。

正如構建設計差的汽車一樣,將來會變得難以理解及需要支付昂貴的維修費用,代碼也是一樣的。

即使這輛汽車可以在你注意到這個錯誤之前跑個幾英里。

原文鏈接:Why Do You Need To Know Interface Fundamentals?

推薦閱讀:

  • 請學習編程

推薦閱讀:

200行代碼實現web框架(三):動手寫個模板引擎
nodejs 和 homebridge
2 列縮進相比 4 列縮進有什麼壞處?

TAG:软件工程 | 编程 |