漫谈软件系统中的形式结构

摘要:
软件系统的难点在于其形式结构的复杂性。大量数据通常在软件系统中流动,需要一个有效的数据结构来组织和容纳大量数据,这样数据就可以按照一定的算法进行批量处理。此外,RDD具有分布式部署特性,可以进行分区。它适用于计算集群中的计算,以获得更好的速度。控制结构指令之间的形式结构构成了软件系统的控制结构,如下图[2]所示。重用是软件系统构建和软件工程的核心概念之一。

温故而知新。

引子

软件系统的功能是与数据打交道。包括:数据的采集、解析、传输、组织、存储、转换、展示。其逻辑单元是加法复制传输。软件的功能简单明了,是数据使用场景赋予了实际的意义。

软件系统的难点在于其形式结构的复杂性。软件的主要形式元素有:数据指令。其形式结构主要有:数据与数据组合的数据结构、数据与指令结合的指令集、指令与指令组合的控制结构

本文来探讨下软件系统中的形式结构。

常用形式结构

形式关系

“学习系统方法论:开篇” 一文中,谈到:软件的形式关系,主要有如下:逻辑关系、序列关系、映射关系、引用关系、互补关系、连接关系、交换关系、层次关系。原子数据和原子指令,通过这些形式关系组合成更大层次的数据结构和控制结构。

数据结构

数据与数据之间的形式结构,构成了软件的数据结构。在软件系统中流转的通常是大量数据,需要有效的数据结构来组织和容纳大量数据,从而能够根据一定的算法批量处理数据。软件的常用数据结构,如下图所示[1]。箭头上层部分展示了不同层次上的常用数据结构,箭头下层部分展示的是控制结构的基本构建元素,在接下来的小节会谈到。
漫谈软件系统中的形式结构第1张

适用场景

多种多样的数据结构,往往是为了适配应用场景的高效率处理而设计的。主要考虑的是存储容量和处理效率,在算法上体现在时间复杂度空间复杂度。比如,位图用来表示映射;元组用来表示有意义的数据对;数组、链表、集合、映射,是为了批量处理数据;栈、队列是为了匹配特定的操作;二叉树是为了处理层次性的组织结构;图是为了处理复杂网状结构;多维数组用来处理科学计算的矩阵;正则表达式,是为了实现文本的模式匹配;模板,是为了描述可复用的文本;JSON 和 XML 主要考虑数据交换的标准化 。

数据建模

数据建模是根据需要对采集到的大量数据项赋予意义有序化组织,从而更好地批量存储和处理数据。建模的主要依据:

  • 数据项之间的属性关联。比如人的健康信息,身高、体重、心跳等;
  • 数据项之间的映射关系。比如 1:1, 1:N, N:N 。 一个人有多个地址,一个地址也可能对于多个人。

业务实现的核心,往往是构造并存储适宜形式的数据。业务核心建立在数据建模的基础上。

海量数据

对于海量数据的存储与组织,主要有结构化的记录集合和非结构化的K-V。记录相对具有固定的字段定义和格式,有规范的设计范式,典型实现是关系数据库;K-V 的字段定义和格式是不固定的,可动态添加和删除,可用来实现海量数据的存储、缓存与锁。

流不是新的数据结构,而是一种数据结构形态。相比于长度固定的数据结构,流是一种长度可变的可无限长的可延迟处理的惰性数据结构。惰性,是指仅当需要处理时才取出数据。流通常用于实时处理。

流的常用操作有 take, first, filter, map, flatMap, foreach, count, distinct, sort, aggregate, collect, group, reduce, fold, join, partition, parallelize, persist, unpersist 等。

RDD

RDD 是不可变的分布式弹性数据流。从 Spark 借用过来的概念。RDD 是一种流,适用于流的操作亦适用于 RDD 。此外,RDD 具有分布式的部署特性,可以进行分区,适合在计算集群中计算,获得更佳的速度。

控制结构

指令与指令之间的形式结构,构成了软件系统的控制结构,如下图所示[2]。控制结构清晰地体现了指令形式结构的层次性。这种层次性建立在多层次复用的基础上。复用是软件系统构建和软件工程中的核心概念之一。

函数、组件、框架、模块、微服务、云服务是在各个层次上的可复用实体。在每个层次上,互补、连接、交换关系都是大规模复用的基本形式关系,在组合术语上略有侧重和不同。比如函数级别上,侧重调用、委托和级联;组件级别上,侧重装配、重组和替换。函数级别更多通过内部实现优化功能;组件级别更多的是重组和替换,而不是修改组件内部实现。交换是这些可复用的实体之间相互交换数据和信息的关系。

软件的组合结构:从指令到软件

函数

函数是带有入参签名返回值的具名化的算法,是基本的功能单元,是低层次的复用级别。为了高程度的复用,函数的设计应遵循如下基本准则:

  • 单一短小。每个函数应当只具备单一的功能和短小的形式,容易理解和维护,作为高程度复用的基础;
  • 正交组合。正交性是指函数功能具有严格不重叠的互补关系。正交组合是高程度复用的基础;
  • 原子化。 函数的入参应当尽量原子化,反映其必需。任何一个多余的入参,都会削弱函数的通用性。
  • 反映意图。函数的签名应当反映意图,而不是实现细节;表达做什么,不是怎么做。

函数式编程,即是以函数为逻辑功能单元,通过大量单一短小函数的批量构建、组合而构建大规模程序的编程理念。可参阅:““完全”函数式编程”“函数工厂Curry的威力”“精练代码:一次Java函数式编程的重构之旅”

构建单元

组件是具有接口和行为的高层次的功能单元,是高级别的复用和构建形式。组件需要仔细设计接口,其基本准则与函数相同,同时在组件行为上定义标准和规范,便于装配、重组和替换。

框架是通过组件装配和交互而构建的高层次控制结构模板和半成品。框架使用者只要遵循框架的规范和约定,在模板里面填充有针对性的业务实体和逻辑即可(特化)。在使用框架时,应当定义系统与依赖的边界及接口,在系统与框架依赖之间建立适配层,在需要替换过时框架时,只需要更改适配层而不需要大动筋骨。这一准则适用于处理任何外部依赖。

模块微服务是接近应用级别的高层次的构建单元,往往是基于框架和组件而构建。其复用性相对较低,更适合于构建完整的应用服务。模块和微服务,更多侧重能够并发执行和分布式的部署与通信,且要高稳定性,因此,侧重在行为的定义和规范上要更加严格,避免频繁变更。

设计模式

在面向对象的语境中,数据结构与控制结构“变身”为对象的状态行为。面向对象设计和编程的核心是对象职责的分配和交互,而设计模式是在特定目标下的对象职责与交互的设计成例。熟悉设计模式,是设计之登堂入室的良好姿势。

设计模式的重点在于关注点结构特征。模式适用于什么场景,通过怎样的结构来构建 ? 从形式结构的角度来看,设计模式的形式元素主要是对象的行为及交互。对象的行为及交互构成了设计模式的骨架,而接口、继承与多态提供了底层的技术机制。

对象的基本行为与交互主要有:创建、复制、委托、继承、包装、选择、组合、聚合、迁移、共享、同步、访问、存储、引用、中介。

  • 创建:从无生有,生产创造。比如工厂模式。
  • 复制:从现有对象中创造新的对象。比如原型模式。
  • 委托:委托于能者,不费己心。比如适配模式。
  • 包装:在现有能力上添砖加瓦,或者以适当形式输出现有能力。比如装饰器模式、代理模式、命令模式。
  • 选择:用不同的策略来完成同一件事,各有优劣,视场景而选择。比如策略模式。
  • 组合:将多层次的具有同一能力的局部事物组合为整体。比如组合模式。
  • 聚合:将大量子能力和复杂交互组合成一个简洁的整体能力输出。比如外观模式、责任链模式。
  • 迁移:不同的状态对应不同的行为,在多种状态和行为之间相互转化。比如状态模式。
  • 共享: 共享大量原子对象和原子数据或共享资源和能力。比如享元模式、模板模式。
  • 同步: 将变化推送给关注者。比如观察者模式。
  • 访问: 提供一种内部访问或全局访问机制。比如迭代器模式、访问者模式、单例模式。
  • 存储: 存储历史状态。比如备忘者模式。
  • 引用: 将一种抽象和接口引向一种实现。比如桥接模式。
  • 中介: 在多种行为的复杂交互之间充当中间协调者,简化交互关系。比如中介者模式。

设计模式往往是这些基本行为和交互的组合。24种常用设计模式可参阅文章[3]。

开发友好的形式结构

软件系统的底层执行,即是指令序列的执行。 对开发人员来说,在适当层次构建可理解可维护的逻辑块,是关键的问题。

对于业务系统来说,可遵循如下方式:

  • 领域实体。 实体是针对领域内的事物的建模。领域实体具有属性和行为。

  • 业务点。 业务点是针对业务判断的原子逻辑块。比如判断订单是否已支付、订单打标等。业务点通常只涉及极有限的业务变量,不依赖外部系统。

  • 业务组件。业务组件是在业务点的基础上,构建起的具有一定业务功能的组件。比如关闭订单、对订单商品退款、扣减库存、交易信息落库。业务组件通常要符合一定的约束,采用一定的技术手段。比如调用外部服务获取数据,对订单的操作加锁,满足操作事务。

  • 组件编排。有了业务组件之后,需要一个组件编排系统,定义若干阶段,在每个阶段能够执行相应的组件序列。通过执行组件序列,来完成整体业务流程。组件编排应当支持扩展点。

  • 事件编排。如果多个行为具有级联特征,则可采用事件编排,组件变化触发事件,产生事件消息,监听事件消息实现业务组件处理,业务组件处理又触发新的事件,如此循环。

  • 交互接口。当涉及多个系统的交互时,需要定义系统之间的交互接口。交互接口往往反映了系统所在领域的最简洁最核心的概念。

技术的实质是实现特定约束而设计的形式结构。比如事务、幂等、限流、并发、异步、缓存、降级、切面等。


小结

本文主要探讨了软件系统中的基本形式结构:数据结构和控制结构。在此基础上,讨论了设计模式这种特定语境的形式结构,以及对开发技术友好的形式结构构建方法。

软件的形式结构语言,为软件的设计和架构提供了扎实的思想基础。在通悉形式结构的基础上,就可以进一步学习和融合各种技术,在具体技术中积累和丰富形式结构,提升设计和架构能力。

关联文章

免责声明:文章转载自《漫谈软件系统中的形式结构》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇20200311 5. Response安装paddle的问题,报错Can not find library: libcudnn.so. The process maybe hang.下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux内核数据结构映射-idr(转)

原文:https://blog.csdn.net/m0_37128231/article/details/96727068?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task 参考链接: linux idr机制...

【数据结构与算法】用go语言实现数组结构及其操作

引言   数组结构是一种很常见的数据结构,并且在大部分编程语言中都存在,这些语言都提供了现成的可以立马就能使用的数组这种数据结构。为了更好的理解数组,这边文章就是来实现数组。 数组的特点   1.内存中数据之间紧密排列在一起。   2.新增元素需要开辟内存空间用以存放新的元素。   3.新增元素时候,如果新增的元素在其他元素中间,那么该新增元素的位置后面所...

【转载】数据结构之图(存储结构、遍历)

一、图的存储结构 1.1 邻接矩阵 图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。 设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 看一个实例,下图左就是一个无向图。 从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij= aji。即从矩...

Java成长之路

怎样学习才能从一名Java初级程序员成长为一名合格的架构师,或者说一名合格的架构师应该有怎样的技术知识体系,这是不仅一个刚刚踏入职场的初级程序员也是工作三五年之后开始迷茫的老程序员经常会问到的问题。希望这篇文章会是你看到过的最全面最权威的回答。 一: 编程基础 不管是C还是C++,不管是Java还是PHP,想成为一名合格的程序员,基本的数据结构和算法基础还...

unity_数据结构(常见数据结构及适用场景)

常见的数据结构: 1.Array: 最简单的数据结构 特点:数组存储在连续的内存上。数组的内容都是相同类型。数组可以直接通过下标访问。优点:由于是在连续内存上存储的,所以它的索引速度非常快,访问一个元素的时间是恒定的也就是说与数组的元素数量无关,而且赋值与修改元素也很简单。缺点:由于是连续存储,所以在两个元素之间插入新的元素就变得不方便。声明一个新的数组时...

MDL

1 先是mdl的数据结构。 2 下面根据用法逐步的讲解mdl数据结构的含义:一般用法,先是 IoAllocateMdl :原型为: 最常用的是VirtualAddress和Length。把自己的NonPageable buffer的起始地址传给IoAllocateMdl ,长度也传给他。可是这个函数具体做了啥呢(下面只是些基本的影响理解的部分,具体更多的...