代码难以理解是软件行业的痼疾. 众多方法和方法论致力于解决这个问题, 不管主观还是客观. 造成理解困难的原因有很多, 我们今天讨论其中一种: 业务流程被分解在代码中, 支离破碎.
而这个原因的引申问题就是: 业务流程在代码中如何组织? 对于这个问题, 争论从未停止:
· Transaction Script vs. Domain Model
· 贫血模型与充血模型之争
· Service存废的争论
造成争论的原因是本质的: OO长于刻画structure, 拙于捕捉behavior. OO在把世界分为多个对象的时候, 把行为也分散了, 我要理解一次交互, 需要在不同的对象的不同方法中跳来跳去. 空间不连续. 有时还用回调,异步等, 时间也不连续了.
尝试
让我们试着跳出软件的范围, 尝试在更广泛的范围内寻找思路, 比如为什么小说和电影有复杂的人物关系和情节, 我们却能轻易理解? 是否跟人理解事物时的Mental Model有关? 如果我们能找出人类理解事物的Mental Model, 据此来编写符合Mental Model的代码是否会提高可理解性? 沿着这个思路走下去, 我们就得到了一种尝试性解决方案: 把世界分解为Data, Context 和 Interaction, 简称DCI
让我们试着从头推导一下.
第一个问题: 当我们错过了开头, 从中间开始看一部电影的时候, 画面上有一个人正在做一件事, 我们会如何入手, 会问什么问题呢?
· 他是谁?
· 他是做什么的?
· 他正在做什么?
这就是我们理解电影剧本或小说的Mental Model: 人物, 角色身份, 然后就是一幕接一幕的场景. 举个例子来说, 电影<<盗梦空间Inception>>中的盗梦团队如下:
· The Extractor(盗梦人)
· The Architect(筑梦师)
· The Point Man(侦察兵)
· The Forger(伪造者)
· The Tourist(旅客)
· The Chemist(药剂师)
盗梦最关键的一步是要在合适的时机穿越回上一层或现实, 电影中叫Kick. 那么 Kick() 这个操作放在哪? 每个人都可以Kick. 这时我们就会想起一个叫做梦主(Dreamer)的角色(Role), Kick应该是Dreamer的操作, 而任何一个人在特定的场景下都可以扮演Dreamer.
class Dreamer {
void Kick();
}
Data
再来看一个稍微贴近软件开发的例子: 转账.
假设储蓄账户的领域模型是一个叫做SavingAccount的class, 它封装了账户余额等属性. 对于如何用它来支持转账操作, 比如取款和存款, 我们至少有两种选择: 我们是仅仅用它来封装简单的余额加减操作, 还是把整个转账流程封装在里面? 也即下面的代码中, Decrease 和 Withdraw 要二选一.
class SavingAccount
{
private Amount balance;
void Decrease(Amount amount) {...}
void Withdraw(Amount amount) {...}
}
从涉及的业务范围, 需要的知识和依赖来看:
· Decrease这个操作, 只涉及到Amount, 所需知识无非是数学上的加减运算
· 而Withdraw, 远远不只是把余额减去多少, 还涉及到事务语义, 用户交互, 恢复, 错误处理, 系统日志以及业务规则, 比如支取额度等. SavingAccount这个类没有能力完成所有的操作
储蓄账户是一个相对稳定的业务概念, 那么简单的Decrease和复杂的Withdraw哪个更能匹配SavingAccount的稳定性呢?
· Decrease是非常稳定的, 它涉及的领域概念无非就是数学上的加减运算
· 而Withdraw发生变化的可能性就大的多, 无论是基础设施的错误处理发生变化, 还是支取额度等规则的变化, 都会导致取款发生变化.
数据模型是相对稳定的, 因此在这里, 我们选择用SavingAccount来表达数据模型, 里面只有Decease等简单的操作数据的方法.
class SavingAccount
{
private Amount balance;
void Decrease(Amount amount) { balance -= amount; }
void Increase(Amount amount) { balance += amount; }
}
Role + Interaction
那么问题来了, 真正的转账操作 Transfer() 放在哪? DCI对此的答案是显式建模, Interaction
交互就自然涉及到Role, 事实上角色是由具体的交互定义的. 如果你不去教课,那么Teacher这个title没有任何意义. 如果你不去跟客户交流, 那么BA这个Role也没任何意义. 换句话说, 只要你在做业务分析,需求分析,此时此刻你就是BA.
那么Transfer涉及到什么角色? Source Account and Destination Account.
Transfer(SourceAccount source, DestinationAccount destination, Amount amount)
{
source.Decrease(amount);
destination.Increase(amount);
...
}
Context
最后一个问题: 谁来指定谁扮演什么角色? DCI的答案是Context
lass TransferContext {
void Transfer(SavingAccount source, SavingAccount destination, Amount amount)
{
var sourceAccount = Cast<SourceAccount>(source);
var destinationAccount = Cast<DestinationAccount>(destination);
Transfer(sourceAccount, destinationAccount, amount); // new TransferInteraction(xxx).Transfer();
}
}
DCI
· Data: What the system is. (static, structure)
· Role + Interaction: What the system does. (dynamic, behavior)
· Context: Mapping the data to role, trigger the interaction. (the director)
推论
推论一, 拆! 把行为拆出去.
什么? OO难道不是要封装数据和行为吗? 让我们考一下古. 最初OO说要封装数据和行为. 所解决的问题是对数据访问无法全面控制而导致的隐藏的Bug, 以及概念的缺失带来的理解上的困难. 但这不意味着要不加辨别的封装所有的数据和行为, 把涉及到某片数据的行为都封装在一起. 事实上我们缺乏仔细的分析而做了过多的封装, 是时候把数据和不合适的行为拆开了, 拆的原则就是稳定性和使用场景
推论二, 类的方法只应该操作自己的数据, 方法参数只应该是基础类型或自己的成员类型.
当你发现两个类的对象有交互从而把交互放在任何一方都会违反上述原则的时候, 定义一个交互类,从而三个类又都满足上面的原则
分享到:
相关推荐
DCI 此存储库包含在Golang和Javascript中实现DCI(数据,上下文,交互)模式的示例。 在撰写这些文章时,我以DCI为动力来学习有关JS和Go的更多信息。 因此,随着我对语言的更多了解以及对DCI的了解加深,我对方法的...
DCI样本 该存储库包含示例代码,该示例代码显示了各种编程语言中的DCI实现。
DCI红外线部署DCI基础结构的剧本和角色。用法 ansible-galaxy install -r installed_roles.yml -p rolesansible-playbook -vv playbook.yml -i <path>/hosts --tags dci-core dci-core标记将仅执行角色的一个子集,...
MPLS L3vrf DCI配置 https://editor.csdn.net/md/?articleId=120409937 DCI互联 Layer 3 VRF-MPLS (EVE vqfx实验)全部配置
我们相信发展必须是一种令人愉快的、创造性的体验,才能真正实现。 Laravel 试图通过简化大多数 Web 项目中使用的常见任务(例如身份验证、路由、会话、排队和缓存)来减轻开发过程中的痛苦。 Laravel 易于访问,但...
通信行业周报:中国电信采购DCI波分设备,开放光网络拉开帷幕
通信行业周报:中国电信采购DCI波分设备,开放光网络拉开帷幕.pdf
Google B4 DCI网络论文,详细介绍B4网络的优点以及实现!
DCI作业的CI管道管理 dci-pipeline命令 dci-pipeline命令允许使用不同的凭据执行多个DCI作业。作业按类型分组。每个组必须成功完成才能开始下一个组。作业及其类型在传递到dci-pipeline命令行的YAML文件中进行了...
兆易单片机gd32F407单片机程序 rtthread + lwip + DCI
平面设计用网上的源码DCI 102:人文科学中的数据 2018 年秋季 // TR 2:45-4:45pm // CGL 212 是活文件。 它将定期更改以反映课程的需要。 接触 Mackenzie Brooks,助理教授兼数字人文图书馆员 // 540-458-8659 // 莱...
包含DCI0-0、DCI0-1、DCI1-0、DCI1-1的解码 输入字节流,解出对应字段的内容 如0-0中。 Nrb_dl_bwp 48 payload bitstring 11010100101110101111111 频域资源分配 11 11010100101 时域资源分配" 4 1101 VRB-to-PRB...
慧点科技DCI.Dec数据交换中心将“电子政务”的基本需求、电子政务安全平台提供的安全机制、数据存储层与应用层的有效隔离有机地结合在一起,使电子政务的应用与其底层的数据结构和存储方式无关。各应用系统与数据...
彭辰阳的讲座PPT :http://www.jdon.com/jivejdon/thread/39722
达梦数据库DM8_DCI 编程指南 OCI接口使用指南
基于DCI标准的数字影院系统,王延生,,本文针对目前数字电影技术国内外现状,提出了一套真正符合数字影院技术规范的数字影院系统,详细介绍了数据码流的处理过程,并在
为了应付大规模云服务和其他不断增长的数据中心存储和处理需求,数据中心系统已越来越分散并且难以管理。诸如人工智能(AI)之类的应用迫切需要低延迟,高带宽的网络架构,以支持服务器之间生成的大量机器对机器输入...
背后的代码,用于生成 DCI 注册表。 直接链接 有兴趣为您的锦标赛或您的套牌构建应用程序直接链接到 ? 超级简单! 只需设置以下 GET 参数即可自动填充字段: firstname :参与者的名字(例如April) lastname : ...
GY-906 MLX90614ESF BAA BCC DCI IR红外测温传感器模块温度采集资料
4G、5G网络优化