如何表达和维护大型逻辑

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。

让我们暂时撇开平台、框架、技术、设计模式、对象思想、敏捷开发论等。 追问程序本质。

从本质上来说, 程序就是一系列有序执行的指令集合。 如何将指令集合组织成可靠可用可信赖的软件(逻辑之塔), 这是个问题。

程序 = 逻辑 + 控制。 what to do + when to do.

从编程角度来说, 开发者应对的就是逻辑, 逻辑的表达、组织和维护。 逻辑是事物自此及彼的可接受的序列。指令是逻辑的具体实现形式。

也就是说:

1.   要表达什么逻辑;

2.   如何表达该逻辑;

3.   如何维护该逻辑。

软件的复杂性表现在如何表达和维护交互复杂的大型逻辑上。

设计模式体现的是一种逻辑块交互解耦的方法;

应用框架解决的是应用的通用逻辑流的控制的问题,让开发者更多地聚焦具体业务逻辑上;

开发技术是在具体的应用情境下按照既定总体思路去探究具体问题解决的方法。

我们要解决的是通用的问题: 如何以更不易出错的方式去表达和维护大型逻辑 ?

表达和维护大型逻辑的终极诀窍就是:  将大型逻辑切分为容易消化的一小块一小块, “不急不忙地吃掉”。

 1.  独立无交互的大型逻辑或接口实现

独立无交互的逻辑通常体现为公共库, 可以解决常见或公共的日常任务, 对其他逻辑无任何依赖和交互, 即自足逻辑。

应对独立无交互的大型逻辑的首要方法是分解为若干的容易实现、测试和复用的小块逻辑, 编写和严格测试。

其次是运用成熟的编程模式去表达逻辑, 尽可能复用经过严格测试的可靠的库。

独立无交互的大型逻辑通过切分逻辑块、严格的单元测试可以获得充分的测试和可靠度。

《编程语言与可复用性》 说明了编程语言如何支持高度灵活的可复用表达。

           2.  独立无交互的耗时长的逻辑或接口实现

响应快速的问题: 用户要求等待时间短 与 请求处理时间耗时长 之间的矛盾导致的。

解决独立无交互的耗时长的逻辑依然可以采用切分逻辑块、严格的单元测试的做法使之更容易处理;

此外, 有两种设计思路可以考虑: 并发 与 异步。

并发思路是将切分的相互独立的逻辑块分配给不同的控制线程中执行, 从而降低请求处理时长; 并发方案获得的性能提升取决于串行操作在总操作中的时间占比。

异步思路是“先响应, 后处理, 终通知” 的"先奏后斩"方案。将一步分离成了三步, 为了让用户首先获得答复, 却增加了新的问题: 消息中间件组件的开发与部署、异步消息发送与接收、编程模型的变化和适应。如果整个过程运作良好, 将会达到很好的体验,容易为用户接受。如果其中一步发生差错, 就会导致各种问题, 比如数据不一致, 消息堆积、 请求无法被处理。最终用户等待时间并没有降低, 反而使体验更加糟糕。 当然, 如果成功率为 95%, 也是“可以接受”的, 这样用户可能会怪自己“运气不太好”, 而不会过多怪责系统的不完善。

并发与异步方案的调试难度都比同步方案增加不少。 每一种新的设计方案都会有其优点, 同时也会有其缺点。 权衡优缺点, 择善而从之 。

注意, 并发方案是针对服务端实际处理请求逻辑而言, 而异步方案是针对请求处理之前是否立即回复的方式; 并发与顺序、 异步与同步两两组合, 可得到四种方式:

顺序同步:  最初的编程模型;  优点是简单、安全、 容易维护和调试;  缺点是性能较低, 响应时间和吞吐量都不高; 若请求处理时长非常短, 采用顺序同步的方案佳;

并发同步:  改进的编程模型; 优点是通过并发提高服务端的处理速度和吞吐量, 但若请求处理耗时较长, 响应时间仍然不高, 影响客户端体验; 若通过并发方案处理请求的时长非常短, 或客户端体验要求不高, 可以采用并发同步的方案;

顺序异步:  改善客户端体验的编程模型; 优点是提高了响应时间和客户端体验, 由于其逻辑处理仍然采用顺序方式,  请求处理时长并未有改善, 因此吞吐量并没有改善。 是一种较好的折衷方案; 若请求处理耗时较长, 影响客户端体验, 且请求处理逻辑复杂, 采用并发方案容易出错或难以并发, 可采用顺序异步方案;

并发异步: 同时改善客户端体验和服务端处理速度; 优点是提高了响应时间、客户端体验和处理速度、吞吐量。 缺点是容易出错, 且不易调试; 若客户端对响应体验要求较高, 请求处理逻辑简单(比如简单的数据拉取和汇总), 采用并发方式可有效提升处理速度, 可以采用并发异步方案;

3.  有交互耦合的小块逻辑或接口实现

软件的复杂性真正体现在逻辑块的持续长久的交互上。这是软件开发与维护中极具挑战性的部分。

有交互的逻辑块通常体现在对可变共享资源的访问上。 交互耦合实际上是共享可变导致的。

一种方法是实现并发互斥, 安全但性能比较低; 另一种方法是通过可接受的复制开销,实现不可变资源的共享。

逻辑之间的交互耦合应该交给交互解耦模块去完成, 而不是在自己的接口里实现。 也就是说, 只有交互解耦模块知道所有接口之间的交互, 而接口只做自己知道的事情就可以了。否则, 接口 A 与接口 B 必须知道彼此究竟做了什么, 才能正确地做自己的事情。

假设 接口 A 和接口 B 都修改某个资源的状态。 接口 A 在做某项操作执行必须执行 IF (ConditionX) do something ; DoMyOwnThing ;  接口 B 也要根据 A 的逻辑相应地执行 if (ConditionY) do anotherThing ; DoMyOwnThing. 耦合的接口数量越多, 或者耦合接口之间的耦合资源越多, 对后期维护和扩展将是一个难以应对的噩梦。

对于逻辑块之间的交互解耦, 或者通俗地说, 模块解耦,  您有怎样的高见, 敬请提出!

程序中的逻辑主要是三类:

1.  获取值: 从数据库、网络或对象中获取值。 如果数据库或网络访问足够稳定的话, 可以看成是简单的获取值, 数据库访问和网络访问对获取值是透明的;

2.  检测值: 检测值是否合法, 通常是前置条件校验、 中间状态校验和后置结果校验;

3.  设置(拷贝)值: 设置数据库、对象中的值; 或者发送数据和指令给网络。如果数据库或网络访问足够稳定的话, 可以看成是简单的设置值, 数据库访问和网络访问对设置值是透明的;

这三类逻辑可以称为逻辑元。 具体业务逻辑就是将逻辑元的组合封装成逻辑块, 有效控制逻辑块的时序交互和资源分配。 时序控制不合理和资源缺乏导致错误和异常。 网络通信错误, 是因为网络带宽资源是有限的。 两个程序同时更新一个共享变量, 如果时序不控制, 就会导致错误的结果。

如何应对异常 ?  请参考 《如何使错误日志更加方便排查问题》,  仔细总结了软件错误产生的各种原因及如何预防和定位。 当然, 还有一些复杂的软件错误, 比如事务与并发, 限于开发经验尚浅, 还给不出有效的方案和措施, 需要根据实践学习和深化。

在已确定的设计方案和业务逻辑的情况下,  如何编写BUG更少的代码:

简明扼要的注释 + 契约式编程 + 更短小的逻辑块 + 复用公共库 + 严格测试

1.   在方法前面编写简明扼要的注释: 方法用途, 接收参数,  返回值, 注意事项, 作者, 时间。

2.   契约式编程: 在方法入口处编写前置条件校验,在方法出口处编写后置结果校验 ;

3.   编写和保持短小逻辑块, 易于为人的脑容量一次性处理, 容易测试;

4.   复用经过严格测试的可靠的公共库; 如果库没有经过很好的测试,但有很好的用处, 帮助其添加测试;

5.   对所编写的代码, 如果不是逻辑元, 都要进行严格测试。

时间: 2024-10-16 20:41:51

如何表达和维护大型逻辑的相关文章

语言与可复用性

编写更少BUG 程序的一个技巧是, 尽可能复用经过严格测试的可靠成熟的公共库. 当我们实现可复用的目标时, 通常是希望尽可能少的代码能够表达更强的复用性, 而不要受到与问题无关因素的制约. 有时, 语言的设计会对可复用性的实现有很大影响. 例子:  取出对象列表中指定字段的值集合. 使用 Java 实现是这样的: package com.qinshuq.zk.study; import java.lang.reflect.Field; import java.util.ArrayList; im

Java的业务OA幸运飞艇平台出租逻辑验证架fluent-validator

在互联网OA幸运飞艇平台出租haozbbs.comQ1446595067 行业中,基于Java开发的业务类系统,不管是服务端还是客户端,业务逻辑代码的更新往往是非常频繁的,这源于功能的快速迭代特性.在一般公司内部,特别是使用Java web技术构建的平台中,不管是基于模块化还是服务化的,业务逻辑都会相对复杂. 这些系统之间.系统内部往往存在大量的API接口,这些接口一般都需要对入参(输入参数的简称)做校验,以保证:1) 核心业务逻辑能够顺利按照预期执行.2) 数据能够正常存取.3) 数据安全性.

大型网站的灵魂——性能

在前一篇随笔     大型网站系统架构的演化中,介绍了大型网站的演化过程,期间穿插了一些技术和手段,我们可以从中看出一个大型网站的轮廓,但想要掌握设计开发维护大型网站的技术,需要我们一步一步去研究实践.所以我打算写一个系列,从理论到实践讲述大型网站的点滴,这也是一个共同学习的过程,希望自己能坚持下去.系列大概会分为两部分,理论和实践,理论部分尽量通俗易懂,也要讲一些细节.实践部分会抽取一些技术做实践,将方法.解决问题过程分享出来.本文将讲述大型网站中一个重要的要素,性能. 什么是性能 有人说性能

iOS开发之聊天模块--内容保存逻辑实现

需求详解: 在实际开发中,有可能是在后期优化的时候,会有这么需要优化的需求:聊天输入框保存之前输入的文本,提高用户的良好体验. 在聊天模块中,用户可能会在输入框输入若干字符,但是没有点击发送就点击退出聊天,或者要点击用户头像确认用户的信息,或者比如需要向好友发送另一个好 友的ID不得不暂时退出当前好友聊天界面跳转找到别的界面找ID,然而当前聊天输入框也已经输入好了若干字符,用户当然不希望退出之后就删除之前输入好的 文字.所以这里就需要暂时保存用户输入好的但是没有发送出去的字符串. 但是,还需要满

大型网站的灵魂——性能[转]

前言 在前一篇随笔<大型网站系统架构的演化>中,介绍了大型网站的演化过程,期间穿插了一些技术和手段,我们可以从中看出一个大型网站的轮廓,但想要掌握设计开发维护大型网站的技术,需要我们一步一步去研究实践.所以我打算写一个系列,从理论到实践讲述大型网站的点滴,这也是一个共同学习的过程,希望自己能坚持下去.系列大概会分为两部分,理论和实践,理论部分尽量通俗易懂,也要讲一些细节.实践部分会抽取一些技术做实践,将方法.解决问题过程分享出来. 本文将讲述大型网站中一个重要的要素,性能. 什么是性能 有人说

大型网站的灵魂&mdash;&mdash;性能

前言     在前一篇随笔<大型网站系统架构的演化>中,介绍了大型网站的演化过程,期间穿插了一些技术和手段,我们可以从中看出一个大型网站的轮廓,但想要掌握设计开发维护大型网站的技术,需要我们一步一步去研究实践.所以我打算写一个系列,从理论到实践讲述大型网站的点滴,这也是一个共同学习的过程,希望自己能坚持下去.系列大概会分为两部分,理论和实践,理论部分尽量通俗易懂,也要讲一些细节.实践部分会抽取一些技术做实践,将方法.解决问题过程分享出来. 本文将讲述大型网站中一个重要的要素,性能. 什么是性能

逻辑卷管理,swap和磁盘配额

磁盘管理 添加新的文件系统 分区,当操作系统已经存在,添加新的分区 一般系统装载本地磁盘里面 实验: 一:我们先添加一块硬盘 一般添加了磁盘之后,真机里面叫/dev/sdb   /dev/sdc ,在虚拟机里面会发现是/dev/vdb /dev/vdc 好了,现在我们已经添加好了硬盘了. 我们要识别硬盘 我们发现的硬盘是/dev/sdb 二:分区 用 fdisk命令 他会跳出一个警告 在企业版6 的时候推荐使用 cu 即: 这二者的区别是,加cu ,代表着是扇区,前者是柱面,都行 使用m 帮助

计算机程序的思维逻辑 (8) - char的真正含义

看似简单的char 通过前两节,我们应该对字符和文本的编码和乱码有了一个清晰的认识,但前两节都是与编程语言无关的,我们还是不知道怎么在程序中处理字符和文本. 本节讨论在Java中进行字符处理的基础 - char,Java中还有Character, String, StringBuffer, StringBuilder等类进行文本处理,他们的基础都是char,我们在后续文章中介绍这些类. char看上去是很简单的,正如我们在第2节所说,char用于表示一个字符,这个字符可以是中文字符,也可以是英文

【长期更新】提升表达能力的二三事

个人微博号:小薇子多一多,这篇是个人的学习篇,您有好的学习思路也可以分享给我,相互学习进步更快. 这篇说的表达是指口语表达.在口语逻辑表达之前,得先问问我自己听懂别人讲话的能力怎样?不好的话,先去学习怎么听懂?这个话题我在另外一篇文章会讲到,你们可以搜一下. 口语表达需要考虑的三要素: 1.听者的信息落差,个性,思维方式和情绪. 2.客观事实即实际存在什么事. 3.我的表达目的是什么,为什么这么表达,是表达什么思想. 但是口语表达和写作表达不同,口语对说话者和听者的大脑要求更高: 我们说话时,传