关注点分离

在计算机科学中，关注点分离（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》有这样的描述：

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

```ruby
class Bear
  def hunt
    forest.select(&:food?)
  end
end
```

bear_eating.rb

```ruby
class Bear
  def eat(food)
    raise "#{food} is not edible!" unless food.respond_to? :nutrition_value
    food.nutrition_value
  end
end
```

bear_hunger.rb

```ruby
class Bear
  attr_accessor :hunger
  def monitor_hunger
    if hunger > 50
      food = hunt
      hunger -= eat(food)
    end
  end
end
```

## 外部链接

- Multi-Dimensional Separation of Concerns
- TAOSAD
- Tutorial and Workshop on Aspect-Oriented Programming and Separation of Concerns