分离关注点

在計算機科學中，**關注點分離**（Separation of concerns，SoC），是將計算機程序分隔為不同部份的設計原則。每一部份會有各自的關注焦點。關注焦點是影響計算機程式程式碼的一組資訊。關注焦點可以像是將程式碼優化過的硬體細節一般，或者像實例化類別的名稱一樣具體。展現關注點分離設計的程序被稱為模組化程序。模組化程度，也就是區分關注焦點，通過將資訊封裝在具有明確界面的程序代碼段落中。封裝是一種資訊隱藏手段。資訊系統中的分層設計是關注點分離的另一個實施例（例如，表示層，業務邏輯層，數據訪問層，持久數據層）。

關注點分離，是對只與「特定概念、目標」（關注點）相關聯的軟體組成部分進行「標識、封裝和操縱」的能力，即標識、封裝和操縱關注點的能力。是處理複雜性的一個原則。由於關注點混雜在一起會導致複雜性大大增加，所以能夠把不同的關注點分離開來，分別處理就是處理複雜性的一個原則，一種方法。分離關注點使得解決特定領域問題的程式碼從業務邏輯中獨立出來，業務邏輯的程式碼中不再含有針對特定領域問題程式碼的調用（將針對特定領域問題程式碼抽象化成較少的程式碼，例如將程式碼封裝成 function 或是 class），業務邏輯同特定領域問題的關係通過側面來封裝、維護，這樣原本分散在整個應用程式中的變動就可以很好的管理起來。

關注點分離的價值在於簡化計算機程序的開發和維護。當關注點分開時，各部份可以重複使用，以及獨立開發和更新。具有特殊價值的是能夠稍後改進或修改一段代碼，而無需知道其他部分的細節必須對這些部分進行相應的更改。

## 實作

編程語言提供的物件導向設計或模塊化編程機制，就是允許開發人員提供 SoC 的機制。例如，C#，C++，Delphi 和 Java 等物件導向的編程語言可以將關注點分解為物件，像 MVC 或 MVP 這樣的架構設計模式，將內容從呈現和數據處理（模型）與內容分開（呈現與內容分離）。服務導向的設計可將關注點分解為服務。諸如 C 和 Pascal 之類的程序式編程語言可將關注點分成過程或函數。面向切面編程語言可以將關注點分解為方面和對象。

在許多其他領域，例如城市規劃、建築或信息設計，分離關注點也是一個重要的設計原則。目標是更有效地理解，設計和管理許多功能相互依存的複雜系統，以便功能可以重用，獨立於其他功能進行優化，並且避免其他功能的潛在故障。常見的例子包括將一個空間分隔成多個房間，這樣一個房間的活動不會影響其他房間的人；或是配電將爐子保持在一個電路，而燈光則保持在另一個電路上，這樣爐子的超載就不會影響燈光。房間分隔的例子顯示了封裝，其中一個房間內的資訊（無論有多混亂）不會用於其他房間，除非通過界面（門是接口）。電路的例子表明，一個模組內部的活動是一個電力消費者附加的電路，不會影響不同模塊中的活動，因此每個模組不會額外去關注另一個模塊發生的情況。

## 起源

這個概念是 1974 年，艾茲赫爾・戴克斯特拉在他的文章《On the role of scientific thought》中提出的。

> 讓我告訴你，對我來說所有聰明的思考的共通特性是什麼。一個人要有系統地深入研究一門課題；必須將這們課題獨立出來，記住在任何時候都只能關注其中一個方面。 比如說，我們知道一個程式必須是正確的，因此我們可以只抓這個點來研究；我們同時也清楚它應當是高效率的，我們可以改天來研究它的效率，等等。我們也可以問自己，程式是否是可取的（desirable）？如果是，為什麼？相反的，同時應對好幾個個方面不會得到任何結果！這就是我有時提到的「the separation of concerns（關注點分離）」。這個技巧就算不是完美可行的，也仍是我知道有效地組織思維的唯一可用技巧。這是就是我說的「將一個人的注意力集中在幾個方面上」。這並不是說忽略其他方面，只是表明從這個方面來看，其他方面並無關緊要這一事實。這即是同時擁有單任務和多任務思維。
> 
> 

15 年之後，這個概念已經被人們所接受。1989 年，Chris Reade 寫的《Elements of Functional Programming》有這樣的描述：

> 一個程式在執行的時候一定會同時做以下幾件事情：
> 
> 1. 描述所要解決的問題
> 2. 按照計算的順序分成幾個部分執行
> 3. 同時處理內存管理
> 

Reade 接著說，

> 理想情況下，程式設計師應該只關注第一個問題（所要解決的問題），因為這個問題是更應該被關注。很明顯的，我們可以通過解決重要的問題來得到更可靠的結果。
> 
> 分離關注還有其它的好處。比如，程式可以分離內存管理和執行順序。然後我們只去一步步的解決問題，不管機器的物理架構。當我們用高速平行的機器或者分布式系統的時候，只需要改動很小的一部分。
> 
> 這就意味著程式語言的實現者必須在不同的機器和機制下，實現相關的功能。
> 
> 

## 例子

### 網際網路協議堆疊

關注點分離是網路設計中的重點。在 TCP/IP 協議族的設計時，有許多心力用在關注點分離，因此有良好定義的 OSI 模型。這可以讓通訊協定的設計者專注在每一層的關注點，不考慮其他層的影響。例如應用層的協定，關注的是如何將郵件資料在可靠的傳送服務上傳輸的細節（一般會是傳輸控制協議），不會關注傳輸控制協議旳細節。TCP 不會關注資料封包的路由，路由是由網路層處理的內容。

### HTML，CSS 和 JavaScript

HTML、層疊樣式表（CSS）和 JavaScript（JS）是開發網頁及相關服務時會用到的語言，彼此的機能是互補的。HTML 主要是用在網站內容的結構、CSS 是用在內容呈現方式的定義、JS 定義網頁和用戶互動的方式，以及網頁的行為。以往的設計不是如此，在導入 CSS 之前，HTML 同時要定義網頁的內容以及顯示方式。

### 主題導向的編程

主題導向的編程可以用分開的軟體結構來處理關注點分離，每一個關注點之間都是平等的。每一個關注點會有自己的類別結構，這些類別結構組成物件、也會提供狀態和方法給複合各關注點的結果。相依性關係會描述這些不同關注點中類別和方法，彼此之間的關係，讓許多關注點可以聯合產生複合式的行為。多維度關注點分離（Multi-dimensional Separation of Concerns）可以用多維「矩陣」的方式來進行各關注點之間的分析及複合，每一個關注點提供一個維度，上面會列舉各個點，其中的矩陣元素會有適當的軟體工件（software artifacts）。

### 面向切面的程序設計

面向切面的程序設計可以將橫切關注點視為主要關注點進行處理。例如，大部份旳軟體都需要某程度的安全性及數據記錄。安全性及數據記錄一般會視為次要關注點，主要關注點一般是實現業務目標。不過在設計程式時，其安全性需要在一開始就考慮進來，而不是視為次要關注點。若在程式開發後再考慮安全性，多半會有安全模型不足的問題，會有很多後續被攻擊的風險。這可以用面向切面的程序設計來解決。例如，有一個切面可以寫成強制呼叫特定 API 時一定要記錄，或是在丟出例外時，一定要記錄錯誤，不論哪一段程式的程式碼丟出錯誤或是傳播錯誤，都不會遺漏。

### 人工智慧中的分析水準

在認知科學及人工智慧中，常常會用到大衛・馬爾的 levels of analysis。研究者可以專注在 (1) 需要計算人工智慧的哪一個層面 (2) 使用的演算法 (3) 演算法在硬體中實現的情形。關注點分離類似軟體工程及硬體工程中的介面 / 實現的差異。

### 規範化系統

在規範化系統（normalized system）中，關注點分離是四個指導原則之一。堅持此一原則可以減少組合性的效應。組合性的效應會在維護軟體時，漸漸的進入系統中。在規範化系統中，可以用工具積極的支持關注點分離。

### 關注點分離和部份類別

關注點分離可以用部份類別的方式實現

#### 關注點分離和 Ruby 中的部份類別

**bear\_hunting.rb**

**bear\_eating.rb**

**bear\_hunger.rb**

## 相關條目

* 抽象原則 (程式設計)
* 面向切面的程序設計
* 關注點
* 主關注點
* 耦合性 (計算機科學)
* 橫切關注點
* 整全觀
* 模組化設計
* 模塊化編程
* 呈現與內容分離
* 單一功能原則
* 機制與策略分離
* 保護和安全分離

## 參考資料

## 外部連結

* Multi-Dimensional Separation of Concerns （頁面存檔備份，存於網際網路檔案館）
* TAOSAD （頁面存檔備份，存於網際網路檔案館）
* Tutorial and Workshop on Aspect-Oriented Programming and Separation of Concerns （頁面存檔備份，存於網際網路檔案館）