类型流(TypeFlow)——世俗化的函数式编程和改进的过程式设计

简介: 类型流(TypeFlow)——世俗化的函数式编程和改进的过程式设计

众所周知,客户需求的自然形态是面向过程(或者叫结构化编程)的。 你在任何项目上,跟业务专家聊需求,你得到的都是先做什么、后做什么;流程、子流程。


至于面向对象,是一种设计方法,并不是所谓最接近现实世界的设计思想,反而是设计师硬凹过来的。只不过现在的程序员上学就学的面向对象,受面向对象训练良久,已经忘了面向过程了。不信你可以把你的类图拿给不懂技术的业务需求方,解释给他们听,问问业务专家脑子里的现实世界是不是这样的。


但面向过程毕竟被淘汰了,而且淘汰是有理由的:


image.png


当一个团队多人共同开发一个应用的时候,由于过程之间存在依赖,而每个过程都可以操作任何资源,并且过程和资源的关系不是显式的,这就使不同开发者之间产生互相干扰,而且是隐式的。因此,随着业务和系统复杂度的提高,和开发团队规模的增加,面向过程只能被淘汰。


面向对象强调行为和数据的封装。某种角度来说,相当于说我的资源只有我能碰,你不许碰,你的资源只有你能动,我也不碰。我们之间只能通过公开的接口(或消息)来交互。从类比的角度看,一个微服务有点像面向对象的宏观体现。其内部数据(聚合)只允许本服务自己操作,别的服务只能通过这个服务的API来访问。这样在一定程度上降低了开发者的互相干扰。


但是,一个微服务比较大且复杂的时候,微服务内的开发团队往往也不止一人,这些内部的开发者之间仍然存在着干扰。和原本的面向过程没有本质区别。


另一个问题就是面向对象本身缺乏统一认识,太多争议。贫血模型和充血模型之争,继承和组合之争,静态方法的问题等等,还有很多case by case的争论。一个设计经常被一些人说不OO,同时被另一些人说不实用。Java的面向对象和和AKKA或者说Actor模型所体现的面向对象又不一样。包括和传说的面向对象鼻祖smalltalk(我没有经验)又有区别。


最近这一两年,我们越来越多指导客户团队做DDD落地的咨询项目,当面对客户大规模的厂商团队(而且常常是来自多个厂商)的时候,大量的junior的厂商开发人员是不理解面向对象的,而不同厂商的高级开发人员互相之间以及和我们之间都是没有统一认识的,这一点对大规模应用的维护带来了非常大的麻烦。


而很多人寄予厚望的函数式编程,虽然有很明显的优点,而且内部争议不太多,但是始终无法大规模使用。经过多年的努力推广,我已经深深认识到:如果开发人员必须要理解了Monad、MonadTransformer之类的概念才能用上函数式编程,那么函数式编程注定只能在小圈子里流行。


因此,在去年(2019年11月)我提出了一种新的设计方法论,叫做类型流(TypeFlow)。这种方法论可以算是一种世俗化的函数式编程和改进型的过程式设计。它的思想可以用下面这张图表达。


image.png

类型流有以下特点:

  • 类型流采用了函数式编程的核心概念之一,纯函数来体现业务逻辑。 对副作用部分显式的体现出来,而不是包装在IO Monad里。
  • 类型流以实现端口适配器架构为设计目标,达到业务逻辑和技术基础设施的分离。
  • 同时,类型流提供可视化建模的构造块,使之成为DDD落地使最细粒度的一块拼图,在大量厂商人员的项目上可以作为详细设计的一部分。


类型流设计建模的构造块如下:


image.png


以一个TODO应用为例,创建代办事项设计图如下:


image.png


这个例子较为简单,但已经可以体现出类型流方法论的主要规则:

  • 从可视化模型上就可以看出:共存在4个待实现的函数,其中两个纯函数,参数校验和返回结果包装;一个副作用函数,保存代办事项;还有一个输入端口,即把这个几个函数编排起来完成业务的程序入口。
  • 每个函数有明确的输入输出类型
  • 函数之间通过匹配的输入输出类型连接起来。
  • 输入输出类型使用业务人员能够理解的业务概念,从而符合DDD的要求。
  • 可视化


这些规则带来以下特点:

  • 副作用剥离,有副作用的没有业务逻辑,有业务逻辑的没有副作用。
  • 每个函数都不知道自己的入参从哪里来,自己的输出被谁使用,这几个函数的开发者也不需要知道。只要按照设计实现了函数,就能够拼接起来。
  • 类型的约束在整个流上层层加强,比如参数校验合法的创建代办事项请求和原始的创建代办事项请求所包含的数据可能是完全一样的,但是他们的数据类型是不同的,在强类型的编程语言实现上如果误传参数是编译不过的。使程序很难写错。


这些特点带来以下优势:

  • 由于副作用剥离,业务逻辑内聚在纯函数、输入输出类型和流程本身;副作用函数是与技术基础设施的交互,不包含业务逻辑,因此可以轻松的替换掉,而不影响任何业务逻辑,达成了端口适配器架构的目标。在我的配套工具原型上,可以轻松的从本地文本文件改成Serverless+对象存储而不修改任何业务逻辑。 参见我以前发布的视频
  • 纯函数和副作用函数都变得非常好测。
  • 由于函数之间的低耦合,开发任务可以分配给任何开发人员并行开发。
  • 可视化的模型将系统的实现细节完整保留,为知识的保留和其他开发人员接手代码提供了抓手。
  • 由于类型流图提供了非常多的细节信息,因此可以开发出强大的配套工具支持:
  • 由于每个函数都定义了明确的输入输出,因此可以生成准确的函数骨架,程序员只需要填空
  • 由于区分了副作用函数和纯函数,可以只给副作用函数生成相应的数据库连接或外部系统stub,加强高级程序员对代码实现的控制。
  • 由于类型流图已经提供了足够的信息,入口函数的调用链是可以自动生成的。
  • 由于每个函数都体现为输入和输出类型,因此如果某个输出类型没有得到有效处理(流不完整),是可以自动检测出来的。
  • 可以Diff,看不同版本变了什么


类型流最核心的思想是显式的副作用剥离,通过这一点得到函数式编程的好处,而不引入函数式编程的难点。再放一张图解释一下副作用剥离的常见模式:


image.png


把上图的一个大函数剥离后,得到了纯函数修改业务数据状态,这个函数不需要知道入参是从哪个数据库表里查来的,也不需要知道它自己的输出要被写到哪里去。这个函数变得极其容易单元测试。图上还有两个未连接的输出,工具应该很容易检测出来。


这个图和流程的区别在于:除了样子有点像,他们完全就不是一种东西。流程图里的每一步都是一个过程,这个过程可以做任何事情,操作任何资源,从流程图上是完全看不出它操作了什么资源的。因此流程图完全继承了过程式编程的所有缺点。


◐ 类型流的团队组织


这么长的文章看到这里,大家想必也应该能看出来,类型流是针对大团队、细分工组织设计的方法论。高级程序员团队画类型流图建模,利用工具生成代码骨架,初级程序员填空。高级程序员团队仍然是一个敏捷团队,而初级程序员只有基本的编程要求。甚至单元测试能力也不要求。刚才我虽然说纯函数很容易单元测试,但其实也很容易开发一个配套的测试工具,可以读取文本的输入参数列表和期望返回值列表,可以让专职的测试人员或者BA去填。所以是对高级程序员提供了要求,对初级程序员极大降低了要求的方法论。


◐ 类型流的适用场景



  • 复杂业务+大规模团队。类型流模型直接对应到代码,能够帮助业务系统的长期维护。借用@李磊同学的话来说:厂商项目交付是客户自己工作的开始。
  • 银行核心系统上云。银行核心系统很多运行在主机上的RPG和COBOL是过程式的。如果原样搬,只换种编程语言,则保留了过程式的全部缺点,而运行环境换成了不如主机可靠的分布式环境,后果难料。如果换面向对象重新设计,则等于完全重新设计,难度大、风险大。类型流的副作用剥离加可视化建模是性价比很高的改进方法。
  • Serverless应用。 Serverless(Function as a Service)具有非常多的好处,但是现在业界的使用场景仍然非常局限。我认为主要是业界还没有找到一个完整业务应用应该如何拆解成函数从而在Serverless基础设施上跑起来。而当前各厂商提供的一些流程编排工具和框架都是非常过程式的。都是不适合多人协作大项目的。类型流具有很大的潜力成为拆解Serverless函数的方法论。

从2019年11月宣布以来,由于种种原因,工具方面进展缓慢,但方法论本身在好几个客户那里,作为DDD落地方法的详细设计一级的方法得到了应用,取得不错的效果,也越来越完善。在此介绍给大家参考,欢迎意见和建议。


相关实践学习
【AI破次元壁合照】少年白马醉春风,函数计算一键部署AI绘画平台
本次实验基于阿里云函数计算产品能力开发AI绘画平台,可让您实现“破次元壁”与角色合照,为角色换背景效果,用AI绘图技术绘出属于自己的少年江湖。
从 0 入门函数计算
在函数计算的架构中,开发者只需要编写业务代码,并监控业务运行情况就可以了。这将开发者从繁重的运维工作中解放出来,将精力投入到更有意义的开发任务上。
相关文章
|
4月前
|
机器学习/深度学习 监控 数据可视化
人体跌倒识别检测项目|全流程源码+数据集+可视化界面+一键训练部署
本项目基于 YOLOv8 模型和 PyQt5 图形界面工具,构建了一个 人体跌倒识别系统,旨在通过计算机视觉技术监测老年人等群体的跌倒行为。项目提供了完整的 源码、数据集、训练流程、以及开箱即用的检测程序,确保用户能够快速搭建并部署自己的跌倒识别系统。
人体跌倒识别检测项目|全流程源码+数据集+可视化界面+一键训练部署
|
IDE 数据可视化 Java
5款经典代码阅读器的使用方案对比
代码阅读是技术人的必备技能之一,高效地梳理代码能够极大程度上提高开发人员的工作效率,进一步为业务创造新价值。
13696 0
5款经典代码阅读器的使用方案对比
|
7月前
|
消息中间件 监控 Docker
Docker环境下快速部署RabbitMQ教程。
就这样,你成功地用魔法召唤出了RabbitMQ,还把它和你的应用程序连接了起来。现在,消息会像小溪流水一样,在你的系统中自由流淌。别忘了,兔子们不喜欢孤独,他们需要你细心的关怀,不时地监控它们,确保他们的世界运转得井井有条。
468 18
|
11月前
|
机器学习/深度学习 数据可视化 算法
YOLOv8改进目录一览 | 涉及卷积层、轻量化、注意力、损失函数、Backbone、SPPF、Neck、检测头等全方位改进
YOLOv8改进目录一览 | 涉及卷积层、轻量化、注意力、损失函数、Backbone、SPPF、Neck、检测头等全方位改进
1375 6
YOLOv8改进目录一览 | 涉及卷积层、轻量化、注意力、损失函数、Backbone、SPPF、Neck、检测头等全方位改进
|
机器学习/深度学习 计算机视觉
【YOLOv11改进 - 注意力机制】GAM(Global Attention Mechanism):全局注意力机制,减少信息损失并放大全局维度交互特征
【YOLOv11改进 - 注意力机制】GAM(Global Attention Mechanism):全局注意力机制,减少信息损失并放大全局维度交互特征本文提出了一种全局注意力机制,通过保留通道和空间信息,增强跨维度的交互,减少信息损失。该机制结合3D置换与多层感知器用于通道注意力,卷积空间注意力子模块用于空间注意力。实验结果表明,在CIFAR-100和ImageNet-1K数据集上,该方法在ResNet和MobileNet上优于多种最新注意力机制。
【YOLOv11改进 - 注意力机制】GAM(Global Attention Mechanism):全局注意力机制,减少信息损失并放大全局维度交互特征
|
10月前
|
人工智能 前端开发 Java
DDD四层架构和MVC三层架构的个人理解和学习笔记
领域驱动设计(DDD)是一种以业务为核心的设计方法,与传统MVC架构不同,DDD将业务逻辑拆分为应用层和领域层,更关注业务领域而非数据库设计。其四层架构包括:Interface(接口层)、Application(应用层)、Domain(领域层)和Infrastructure(基础层)。各层职责分明,避免跨层调用,确保业务逻辑清晰。代码实现中,通过DTO、Entity、DO等对象的转换,结合ProtoBuf协议,完成请求与响应的处理流程。为提高复用性,实际项目中可增加Common层存放公共依赖。DDD强调从业务出发设计软件,适应复杂业务场景,是微服务架构的重要设计思想。
|
10月前
|
关系型数据库 Linux 数据库
PostgreSQL 入门指南:安装、配置与基本命令
本文从零开始,详细介绍如何在 Windows、Linux 和 macOS 上安装和配置 PostgreSQL,涵盖30+个实操代码示例。内容包括安装步骤、配置远程访问和用户权限、基础数据库操作命令(如创建表、插入和查询数据),以及常见问题的解决方案。通过学习,你将掌握 PostgreSQL 的基本使用方法,并为后续深入学习打下坚实基础。
11365 1
|
存储 消息中间件 JSON
DDD基础教程:一文带你读懂DDD分层架构
DDD基础教程:一文带你读懂DDD分层架构
|
机器学习/深度学习 PyTorch 算法框架/工具
【YOLOv8改进 - 注意力机制】SimAM:轻量级注意力机制,解锁卷积神经网络新潜力
YOLO目标检测专栏介绍了SimAM,一种无参数的CNN注意力模块,基于神经科学理论优化能量函数,提升模型表现。SimAM通过计算3D注意力权重增强特征表示,无需额外参数。文章提供论文链接、Pytorch实现代码及详细配置,展示了如何在目标检测任务中应用该模块。
|
设计模式 缓存 Devops
微服务架构最强讲解,那叫一个通俗易懂!
微服务架构(Microservice Architecture)是一种架构概念,旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦。你可以将其看作是在架构层次而非获取服务的
33168 3
微服务架构最强讲解,那叫一个通俗易懂!