learn.lianglianglee.com/专栏/领域驱动设计实践(完)/050 领域模型与结构范式.md.html
2022-05-11 19:04:14 +08:00

950 lines
43 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>050 领域模型与结构范式.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/领域驱动设计实践(完)/001 「战略篇」访谈 DDD 和微服务是什么关系?.md.html">001 「战略篇」访谈 DDD 和微服务是什么关系?.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/002 「战略篇」开篇词:领域驱动设计,重焕青春的设计经典.md.html">002 「战略篇」开篇词:领域驱动设计,重焕青春的设计经典.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/003 领域驱动设计概览.md.html">003 领域驱动设计概览.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/004 深入分析软件的复杂度.md.html">004 深入分析软件的复杂度.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/005 控制软件复杂度的原则.md.html">005 控制软件复杂度的原则.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/006 领域驱动设计对软件复杂度的应对(上).md.html">006 领域驱动设计对软件复杂度的应对(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/007 领域驱动设计对软件复杂度的应对(下).md.html">007 领域驱动设计对软件复杂度的应对(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/008 软件开发团队的沟通与协作.md.html">008 软件开发团队的沟通与协作.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/009 运用领域场景分析提炼领域知识(上).md.html">009 运用领域场景分析提炼领域知识(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/010 运用领域场景分析提炼领域知识(下).md.html">010 运用领域场景分析提炼领域知识(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/011 建立统一语言.md.html">011 建立统一语言.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/012 理解限界上下文.md.html">012 理解限界上下文.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/013 限界上下文的控制力(上).md.html">013 限界上下文的控制力(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/014 限界上下文的控制力(下).md.html">014 限界上下文的控制力(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/015 识别限界上下文(上).md.html">015 识别限界上下文(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/016 识别限界上下文(下).md.html">016 识别限界上下文(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/017 理解上下文映射.md.html">017 理解上下文映射.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/018 上下文映射的团队协作模式.md.html">018 上下文映射的团队协作模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/019 上下文映射的通信集成模式.md.html">019 上下文映射的通信集成模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/020 辨别限界上下文的协作关系(上).md.html">020 辨别限界上下文的协作关系(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/021 辨别限界上下文的协作关系(下).md.html">021 辨别限界上下文的协作关系(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/022 认识分层架构.md.html">022 认识分层架构.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/023 分层架构的演化.md.html">023 分层架构的演化.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/024 领域驱动架构的演进.md.html">024 领域驱动架构的演进.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/025 案例 层次的职责与协作关系(图文篇).md.html">025 案例 层次的职责与协作关系(图文篇).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/026 限界上下文与架构.md.html">026 限界上下文与架构.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/027 限界上下文对架构的影响.md.html">027 限界上下文对架构的影响.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/028 领域驱动设计的代码模型.md.html">028 领域驱动设计的代码模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/029 代码模型的架构决策.md.html">029 代码模型的架构决策.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/030 实践 先启阶段的需求分析.md.html">030 实践 先启阶段的需求分析.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/031 实践 先启阶段的领域场景分析(上).md.html">031 实践 先启阶段的领域场景分析(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/032 实践 先启阶段的领域场景分析(下).md.html">032 实践 先启阶段的领域场景分析(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/033 实践 识别限界上下文.md.html">033 实践 识别限界上下文.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/034 实践 确定限界上下文的协作关系.md.html">034 实践 确定限界上下文的协作关系.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/035 实践 EAS 的整体架构.md.html">035 实践 EAS 的整体架构.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/036 「战术篇」访谈DDD 能帮开发团队提高设计水平吗?.md.html">036 「战术篇」访谈DDD 能帮开发团队提高设计水平吗?.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/037 「战术篇」开篇词:领域驱动设计的不确定性.md.html">037 「战术篇」开篇词:领域驱动设计的不确定性.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/038 什么是模型.md.html">038 什么是模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/039 数据分析模型.md.html">039 数据分析模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/040 数据设计模型.md.html">040 数据设计模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/041 数据模型与对象模型.md.html">041 数据模型与对象模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/042 数据实现模型.md.html">042 数据实现模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/043 案例 培训管理系统.md.html">043 案例 培训管理系统.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/044 服务资源模型.md.html">044 服务资源模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/045 服务行为模型.md.html">045 服务行为模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/046 服务设计模型.md.html">046 服务设计模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/047 领域模型驱动设计.md.html">047 领域模型驱动设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/048 领域实现模型.md.html">048 领域实现模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/049 理解领域模型.md.html">049 理解领域模型.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/领域驱动设计实践(完)/050 领域模型与结构范式.md.html">050 领域模型与结构范式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/051 领域模型与对象范式(上).md.html">051 领域模型与对象范式(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/052 领域模型与对象范式(中).md.html">052 领域模型与对象范式(中).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/053 领域模型与对象范式(下).md.html">053 领域模型与对象范式(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/054 领域模型与函数范式.md.html">054 领域模型与函数范式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/055 领域驱动分层架构与对象模型.md.html">055 领域驱动分层架构与对象模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/056 统一语言与领域分析模型.md.html">056 统一语言与领域分析模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/057 精炼领域分析模型.md.html">057 精炼领域分析模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/058 彩色 UML 与彩色建模.md.html">058 彩色 UML 与彩色建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/059 四色建模法.md.html">059 四色建模法.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/060 案例 订单核心流程的四色建模.md.html">060 案例 订单核心流程的四色建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/061 事件风暴与业务全景探索.md.html">061 事件风暴与业务全景探索.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/062 事件风暴与领域分析建模.md.html">062 事件风暴与领域分析建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/063 案例 订单核心流程的事件风暴.md.html">063 案例 订单核心流程的事件风暴.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/064 表达领域设计模型.md.html">064 表达领域设计模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/065 实体.md.html">065 实体.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/066 值对象.md.html">066 值对象.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/067 对象图与聚合.md.html">067 对象图与聚合.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/068 聚合设计原则.md.html">068 聚合设计原则.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/069 聚合之间的关系.md.html">069 聚合之间的关系.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/070 聚合的设计过程.md.html">070 聚合的设计过程.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/071 案例 培训领域模型的聚合设计.md.html">071 案例 培训领域模型的聚合设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/072 领域模型对象的生命周期-工厂.md.html">072 领域模型对象的生命周期-工厂.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/073 领域模型对象的生命周期-资源库.md.html">073 领域模型对象的生命周期-资源库.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/074 领域服务.md.html">074 领域服务.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/075 案例 领域设计模型的价值.md.html">075 案例 领域设计模型的价值.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/076 应用服务.md.html">076 应用服务.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/077 场景的设计驱动力.md.html">077 场景的设计驱动力.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/078 案例 薪资管理系统的场景驱动设计.md.html">078 案例 薪资管理系统的场景驱动设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/079 场景驱动设计与 DCI 模式.md.html">079 场景驱动设计与 DCI 模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/080 领域事件.md.html">080 领域事件.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/081 发布者—订阅者模式.md.html">081 发布者—订阅者模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/082 事件溯源模式.md.html">082 事件溯源模式.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/083 测试优先的领域实现建模.md.html">083 测试优先的领域实现建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/084 深入理解简单设计.md.html">084 深入理解简单设计.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/085 案例 薪资管理系统的测试驱动开发(上).md.html">085 案例 薪资管理系统的测试驱动开发(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/086 案例 薪资管理系统的测试驱动开发(下).md.html">086 案例 薪资管理系统的测试驱动开发(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/087 对象关系映射(上).md.html">087 对象关系映射(上).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/088 对象关系映射(下).md.html">088 对象关系映射(下).md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/089 领域模型与数据模型.md.html">089 领域模型与数据模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/090 领域驱动设计对持久化的影响.md.html">090 领域驱动设计对持久化的影响.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/091 领域驱动设计体系.md.html">091 领域驱动设计体系.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/092 子领域与限界上下文.md.html">092 子领域与限界上下文.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/093 限界上下文的边界与协作.md.html">093 限界上下文的边界与协作.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/094 限界上下文之间的分布式通信.md.html">094 限界上下文之间的分布式通信.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/095 命令查询职责分离.md.html">095 命令查询职责分离.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/096 分布式柔性事务.md.html">096 分布式柔性事务.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/097 设计概念的统一语言.md.html">097 设计概念的统一语言.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/098 模型对象.md.html">098 模型对象.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/099 领域驱动设计参考过程模型.md.html">099 领域驱动设计参考过程模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/100 领域驱动设计的精髓.md.html">100 领域驱动设计的精髓.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/101 实践 员工上下文的领域建模.md.html">101 实践 员工上下文的领域建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/102 实践 考勤上下文的领域建模.md.html">102 实践 考勤上下文的领域建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/103 实践 项目上下文的领域建模.md.html">103 实践 项目上下文的领域建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/104 实践 培训上下文的业务需求.md.html">104 实践 培训上下文的业务需求.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/105 实践 培训上下文的领域分析建模.md.html">105 实践 培训上下文的领域分析建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/106 实践 培训上下文的领域设计建模.md.html">106 实践 培训上下文的领域设计建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/107 实践 培训上下文的领域实现建模.md.html">107 实践 培训上下文的领域实现建模.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/108 实践 EAS 系统的代码模型.md.html">108 实践 EAS 系统的代码模型.md.html</a>
</li>
<li>
<a href="/专栏/领域驱动设计实践(完)/109 后记:如何学习领域驱动设计.md.html">109 后记:如何学习领域驱动设计.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>050 领域模型与结构范式</h1>
<h3>领域模型与建模范式</h3>
<p>即使采用领域模型驱动设计,针对同一个领域获得的领域模型也会千差万别,除了因为设计能力、经验及对现实世界的理解不一致外,对模型产生根本影响的是<strong>建模范式Modeling Paradigm</strong></p>
<p>“范式Paradigm”一词最初由美国哲学家托马斯·库恩Thomas Kuhn在其经典著作《科学革命的结构The Structure of Scientific Revolutions》中提出被用于对科学发展的分析。库恩认为每一个科学发展阶段都有特殊的内在结构而体现这种结构的模型即范式。他明确无误地给出了范式的一个简洁定义</p>
<blockquote>
<p>“按既定的用法,<strong>范式就是一种公认的模型或模式</strong>。”</p>
</blockquote>
<p>范式可以用来界定什么应该被研究、什么问题应该被提出、如何对问题进行质疑,以及在解释我们获得的答案时该遵循什么样的规则。倘若将范式运用在软件领域的建模过程中,就可以认为建模范式就是建立模型的一种模式,是针对业务需求提出的问题进行建模时需要遵循的规则。</p>
<p>倘若要建立领域模型可以遵循的建模范式包括结构范式、对象范式和函数范式恰好对应于三种编程范式Programming Paradigm</p>
<ul>
<li>结构化编程structured programming</li>
<li>面向对象编程object-oriented programming</li>
<li>函数式编程functional programming</li>
</ul>
<p>建模范式与编程范式的对应关系,也证明了分析、设计与实现三位一体的关系。</p>
<h3>结构范式</h3>
<p>一提及面向过程设计,浮现在我们脑海中的大多是一堆负面的贬义词:糟糕的、邪恶的、混乱的、贫瘠的……实际上,面向过程设计就是结构化编程思想的体现,如果追溯它的发展历史,我们发现该范式提倡的设计思想与面向对象编程和函数式编程在本质上并无太大区别。它,并不代表一定是“坏”的设计。</p>
<p>结构化编程的理念由 Edsger Wybe Dijkstra 在 1968 年最先提出。在给 CACM 编辑的一封信中Dijkstra 论证了使用 goto 是有害的,并明确提出了顺序、选择和循环三种基本的结构。通过这三种基本的结构,可以使程序结构可以变得更加清晰、富有逻辑。</p>
<p>结构化编程强调模块作为功能分解的基本单位。David Parnas 在 1971 年发表的论文 <em>Information Distribution Aspect of Design Methodology</em> 中解释了何谓“结构”:</p>
<blockquote>
<p>“所谓‘结构’通常指用于表示系统的部分。结构体现为分解系统为多个模块,确定每个模块的特征,并明确模块之间的连接关系。”</p>
</blockquote>
<p>针对模块间的连接关系,在同一篇论文中 Parnas 还提到:</p>
<blockquote>
<p>“模块间的信息传递可以视为接口Interfaces。”</p>
</blockquote>
<p>这些观点体现了结构化设计的系统分解原则,通过模块对职责进行封装与分离,通过接口管理模块之间的关系。</p>
<p>模块对职责的封装体现为信息隐藏Information Hiding这一原则同样来自于结构化编程。还是 David Parnas他在 1972 年发表论文《论将系统分解为模块的准则》中强调了信息隐藏的原则。</p>
<p>《代码大全》的作者 Steve McConnell 认为:</p>
<blockquote>
<p>“信息隐藏是软件的首要技术使命中格外重要的一种启发式方法,因为它强调的就是隐藏复杂度,这一点无论是从它的名称还是实施细节上都能看得很清楚。”</p>
</blockquote>
<p>信息隐藏在面向对象设计中,其实就是封装和隐私法则的体现。</p>
<p>结构化编程的着眼点是“面向过程”,采用结构化编程范式的语言就被称之为“面向过程语言”。因此,面向过程语言同样可以体现“封装”的思想,如 C 语言允许在头文件中定义数据结构和函数声明,然后在程序文件中具体实现。这种头文件与程序代码的分离,可以有效地保证程序代码中的具体实现细节对于调用者而言是不可见的。当然,这种封装性不如面向对象语言具有更为丰富的封装层次,对数据结构不具有控制权。倘若有别的函数直接操作数据结构,就会在一定程度上破坏了这种封装性。</p>
<p>以过程为中心的结构化编程思想强调“自顶向下、逐步向下”的设计原则。它对待问题域的态度,就是将其分解为一个一个步骤,每个步骤再由函数来实现,并按照顺序、选择或循环的结构对这些函数进行调用,组成一个主函数。每个函数内部同样采用相同的程序结构。以过程式的思想对问题进行步骤拆分,就可以利用功能分解让程序的结构化繁为简,变混乱为清晰。显然,只要问题的拆分合理,且注意正确的职责分配与信息隐藏,面向过程的程序设计同样可以交出优秀设计的答卷。</p>
<p>不可否认的是,面向对象设计是面向过程设计的进化,软件设计人员也在这个发展过程中经历一次编程范式的迁移,即从面向过程迁移到面向对象。为何要从过程进化到对象呢?根本原因在于这两种方法对程序的理解截然不同。面向过程语言 Pascal 的发明人沃斯教授认为:</p>
<p><strong>数据结构 + 算法 = 程序</strong></p>
<p>这一公式概况了面向过程语言的特点:数据结构和变量相对应,算法和函数相对应,算法是用来操作数据结构的。至为关键之处在于,<strong>面向过程设计强调将数据结构与算法分开</strong>,这就会导致:</p>
<ul>
<li>无法直观说明算法与数据结构之间的关系:当数据结构发生变化时,分散在整个程序各处的操作该数据结构的算法都需要修改。</li>
<li>无法限制数据结构可被操作的范围:任何算法都可以操作任何数据结构,就有可能因为某个错误操作导致程序出现问题而崩溃。</li>
<li>操作数据结构的算法被重复定义:算法的重复定义并非故意为之,而是缺乏封装性的必然结果。</li>
</ul>
<p>假设算法 f1() 和 f2() 分别操作了数据结构 X 和 Y。由于粒度的原因X 和 Y 数据结构共享了底层数据结构 Z 中标记为 i 的数据。它们之间的关系如下图所示:</p>
<p><img src="assets/270909e0-9b55-11e9-92b4-9513ba23abe6" alt="img" /></p>
<p>如果 Z 的数据 i 发生了变化,会影响到函数 f1() 和 f2(),但由于我们知道这个变化,因此程序是可控的。然而,由于数据结构与算法完全分离,在这同时还有别的开发人员增加了一个操作底层数据结构 Z 的函数,原有开发人员并不知情。如下图所示的函数 f3() 操作了数据结构 Z 的数据 i却有可能在 i 发生了变化时并没有做相应调整,带来隐藏的缺陷:</p>
<p><img src="assets/58545cc0-9b55-11e9-8d0a-d15698dbb916" alt="img" /></p>
<p>面向对象通过将数据结构与算法封装在一起,使得数据与函数之间的关系更加清晰。例如数据结构 X 与算法 f1() 封装在一起,数据结构 Y 和算法 f2() 封装在一起,同时为数据结构 Z 提供算法 fi(),作为访问数据 i 的公有接口。任何需要访问数据 i 的操作都通过 fi() 算法调用,包括前面提及的算法 f3()</p>
<p><img src="assets/80702770-9b55-11e9-b9ee-7b6a5ae2885d" alt="img" /></p>
<p>显然,倘若 Z 的数据发生了变化,算法 fi() 一定会知晓这个变化;由于 X 和 Y 的算法 f1()、f2() 以及后来增加的 f3() 并没有直接操作该数据,因此这种变化被有效地隔离了,不会受到影响。</p>
<p>正如前面所说,算法的重复定义也很难避免。例如事先定义了 Rectangle 类,它定义了矩形的宽度和长度:</p>
<pre><code class="language-java">public class Rectangle {
private int width;
private int length;
public Rectangle(int width, int length) {
this.width = width;
this.length = length;
}
public int getWidth() {
return width;
}
public int getLength() {
return length;
}
}
</code></pre>
<p>一个几何类 Geometric 类需要计算矩形的周长和面积,因而它会调用 Rectangle</p>
<pre><code class="language-java">public class Geometric {
public int area(Rectangle rectangle) {
return rectangle.getWidth() * rectangle.getLength();
}
public int perimeter(Rectangle rectangle) {
return (rectangle.getWidth() + rectangle.getLength()) * 2;
}
}
</code></pre>
<p>现在,其他开发人员需要编写一个绘图工具,同样需要用到 Rectangle</p>
<pre><code class="language-java">public class Painter {
public void draw(Rectangle rectangle) {
// ...
// 产生了和 Geometric::area() 方法一样的代码
int area = rectangle.getWidth() * rectangle.getLength();
//...
}
}
</code></pre>
<p>由于 Rectangle 类将数据与行为分别定义到了不同的地方,调用者 Painter 在重用 Rectangle 时并不知道 Geometric 已经提供了计算面积和周长的方法,因而会首先想到亲自实现他需要的行为。这就必然造成相同的算法被多个开发者重复实现。只有极其有心的开发者才可能会尽力地降低这类重复。当然,这是以付出额外精力为代价的。</p>
<p>Rectangle 类就是我们在前面提及的贫血对象如果建立的模型皆为这样只有数据没有行为的贫血对象则该模型为贫血模型。Martin Fowler 认为:</p>
<blockquote>
<p>贫血模型一个明显的特征是它仅仅是看上去和领域模型一样,都拥有对象、属性、对象间通过关系关联。但是当你观察模型所持有的业务逻辑时,你会发现,贫血模型中除了一些 Getter 和 Setter 方法,几乎没有其他业务逻辑。这种情况事实上是由一些设计规则中规定不要把业务逻辑放到“领域模型”中造成的,取而代之的是把所有的业务逻辑都封装到众多 Service 中。这些 Service 对象在“领域对象”(领域层)之上形成一个 Service 层,而把“领域对象”当做数据使用。</p>
</blockquote>
<p>如果把操作数据结构的算法都放在一起,并把数据结构隐藏起来,开发者就失去了自由访问数据的权力。如果一个开发者需要计算 Rectangle 的面积,数据访问权的丧失会让他首先考虑的不是在类的外部亲自实现某个算法,而是首先寻求重用别人的实现,从而最大程度地避免重复:</p>
<pre><code class="language-java">public class Rectangle {
// 没有访问 width 的需求时,就不暴露该字段
private int width;
// 没有访问 length 的需求时,就不暴露该字段
private int length;
public Rectangle(int width, int length) {
this.width = width;
this.length = length;
}
public int area() {
return this.width * this.length;
}
public int perimeter() {
return (this.width + this.length) * 2;
}
}
</code></pre>
<p>由于数据与行为封装在了一起当我们调用对象时IDE 还可以让开发人员迅速判断被调对象是否提供了自己所需的接口:</p>
<p><img src="assets/7bb5b730-9b56-11e9-9a7d-eb6640afedb7" alt="img" /></p>
<p>通过这个例子我们也解释了为何贫血模型是结构范式的体现:贫血模型将数据结构与算法分开,无法有效地利用对象的封装能力。</p>
<p>如果按照数据模型驱动设计,很容易获得贫血模型。因为在针对数据库和数据表建模时,数据模型中的持久化对象作为数据表的映射,扮演了数据提供者的角色,这正是 Martin Fowler 所说的“一些设计规则中规定不要把业务逻辑放到‘领域模型’”。</p>
<p>在分层架构中,持久化对象并不属于领域层,而是作为表的映射放在基础设施层(数据访问层)。此时的持久化对象其实并非真正意义上的对象,而是一种数据结构。当然,我们也可以打破这样的规则,将那些操作了持久化对象数据的行为封装到持久化对象中,这时的持久化对象由于包含了领域逻辑,就变成了领域模型的一部分。</p>
<p>因此,模型驱动设计的方法与建模范式并没有必然的联系。即使采用领域模型驱动设计,仍然有可能违背对象范式的设计原则,实现为诸如“事务脚本”这样的过程式代码,这是我们在进行领域模型驱动设计过程中需要时刻警惕的。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/领域驱动设计实践(完)/049 理解领域模型.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/领域驱动设计实践(完)/051 领域模型与对象范式(上).md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997e688eaa3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>