如何了解一个软件的设计?

2023-02-01 17:22:01 浏览数 (1)

刚入职,接手新项目,面对一个全新项目,怎么快速研究它?

很多人直接看源码,一头扎入代码,很快就迷失其中,最初那股子探索精神,也会逐渐被迷茫所替。有多少次你满怀激情打开一个开源项目,结果多半坚持不了就放弃。问题出在哪?迷茫是因为缺少对软件整体了解,如同不带地图指南针就闯入热带雨林,迷路只是早晚。阅读源码是必经一步,却不是第一步。应先从了解软件设计开始。

1 模型、接口和实现

好比你看代码:

  • 模型 先看有哪些类及之间关系
  • 接口 然后打开一个具体类,看提供哪些方法
  • 实现 最后,再打开一个具体方法,看怎么写的

1.1 模型

一个软件的核心部分,也称之为抽象。设计最关键的就是构建出模型。而理解一个设计中的模型,可助我们建立对这个软件整体的认知。

如:

  • 编写分布式计算代码,需考虑怎样在不同节点上调度计算
  • 使用MapReduce,只要考虑如何把计算分开(Map),最后汇总(Reduce)
  • 到了Spark,注意力就集中在要做怎样的计算

它们在解决同样问题,只是抽象层次逐步提高,越来越接近要解决的问题,越来越少考虑计算在不同的机器如何执行,大大降低理解门槛。知道模型的重要性,目光甚至可不局限在某一软件。若把同一个领域不同阶段的多个模型联系起来,还能看到软件发展趋势。

1.2 接口

决定软件通过怎样方式,暴露模型提供的能力。 是我们与这个软件交互的入口。

  • 一个程序库的接口就是它的API,但对同样模型,每个人会设计出不同API,而不同API有不同表达能力。比如:Guava对JDK的一些API重新封装,就为简化开发,而很多优秀的做法后来又被JDK学了回去
  • 一个工具软件一般会提供命令行接口,比如Unix命令行工具就是典型的命令行接口
  • 一个业务系统的接口,就是对外暴露的各种接口,比如,它提供的各种REST API,也可能是提供了RPC给其它系统的调用。 ……

想深入源码,了解一个软件,可从一个接口进入到软件,看它怎样完成各种基本功能。

1.3 实现

软件提供的模型和接口在内部如何实现,这是软件能力得以发挥的根基。

  • 一个业务系统收到一个请求之后,是把信息写到DB,还是转发给其它系统
  • 一个算法实现,是选择调用已有程序库,还是自己实现
  • 一个系统中的功能,哪些应该做成分布式,哪些应该由一个中央节点统一处理
  • 一段业务处理,是应该做成单线程,还是多线程
  • 当资源有竞争,是每个节点自己处理,还是交由一个中间件统一处理
  • 不同系统之间的连接,该采用哪种协议,是自己实现,还是找个中间件 ……

所以,做每一个技术决策都应该结合自己所开发应用的特点,并不存在一个通用的解决方案。实际工作中,许多人以为的设计其实是这里的实现。“实现”很重要,须建立在模型和接口的基础上。一个系统的设计,模型最核心。若模型变了,这个软件便不再是这个软件,而接口通常反映的就是模型。所以,模型和接口的稳定度都要比实现高,实现则随软件发展而不断调整。

  • 模型:需求
  • 接口:可以提供哪些功能
  • 实现:实现模型和接口的办法,语言,框架等技术

在使用类似springboot mybatis开发的时候,mybatis-generator生产的mapper,service,service imp,在配合controller,就可以对数据库的数据进行增删改查,然后就可以实现一些CMS啊电商之类的业务需求,似乎都不需要自己定义新的接口和抽象,请问这是因为业务过于简单的原因吗? 不,这是因为你把业务逻辑混在增删改查里。

2 案例

2.1 Redis

随使用Redis增多,对Redis有进一步的需求。所以,从6.0开始,它开始支持多线程版本,以便于更好地满足需求。但即便Redis改成多线程,它还是那个Redis,它的模型和接口还是稳定不变,只是实现变了。

2.2 CRM

模型,通常包含两类要素:

  • 基本元素 CRM的基本元素就包括项目、客户、合同和回款
  • 这些元素之间的关系 相互之间的主要关系通常是客户报备,进入立项环节(评估投入产出),再签约,最后进入回款

这是基本模型。这个模型(系统)的接口,就是要为BD提供从客户报备到签约、回款的整个流程管理。

实现就是要考虑如何用消息在这些模块之间传递数据,状态控制、数据查重锁定等。

3 设计三步走

严格区分模型、接口和实现,是因为这三者关注点不同,而很多人讨论所谓“设计”,经常把它们混为一谈。

你们团队开会是不是经常有种很混乱感觉?问题就在于你们把不同层面内容混在一起,一起吃做大锅饭,最后那是人吃的吗?

正确做法是在讨论设计时,遵循顺序:先模型,再接口,最后实现。了解一个设计亦如此。

模型没弄清楚,就讨论细节,难分清哪些东西核心,须保留,哪些东西可替换。 若清楚模型,就知道哪些内容在系统中广泛适用,哪些内容须隔离。即分清模型会帮助你限制实现的使用范围。

如下是一个简化后架构图,订单服务完成处理后,通过MQ把消息发给支付服务,支付处理后,再通过MQ把消息发给物流:

这张图问题在哪?把模型和实现混淆。图中的订单、支付和物流,说的都是模型层,但RabbitMQ就把实现层拉进来。RabbitMQ只是实现这个功能时的一个技术选型,即若随业务发展,它不能很好扮演角色,就可替换掉,而整个设计不变。 所以,实现这段代码时,须封装MQ相关代码,不能在系统各处随意调用,因为它属于实现,可能随时被替换。

了解设计时,要按层次去了解,因为设计是分层的。每打开一个层次,需要了解它的内部时,还要按模型、接口和实现顺序研究。 如RocketMQ设计模型https://github.com/apache/rocketmq/blob/master/docs/cn/concept.md。

如os,了解它的内部,就知道它有内存管理、进程调度、文件系统等模块。可按照模型、接口和实现去理解每个模块,如进程管理:

  • 进程管理的核心模型就包括进程模型和调度算法
  • 接口包括,进程的创建、销毁以及调度算法的触发等
  • 不同调度算法就是具体实现

os难以学习,很大程度上就在于,很多人没有搞清楚其中各个概念之间的关系。 即便层层展开到最后,到了一个具体类,甚至是一个具体数据结构,依然可以按照模型、接口和实现结构理解,如很多Java面试题常问到的HashMap:

  • 其模型就是哈希表
  • 它定义了一些接口,比如,get、put等
  • 它的实现原来是用标准的HashMap实现,后来则借鉴了红黑树

再如,当使用一个新库或框架,先看接口,看对外提供功能是否满足要求,然后才是具体实现。 对于模型,想学习开源软件的架构时,再关注。

当能一层层理解设计,就像一棵知识树逐渐展开,每个知识节点在展开时,都会有下级的更具体内容。脑中有这样一棵设计树,就掌握了整个系统地图,再有新需求来,就不会盲目改代码。

4 总结

了解一个软件设计,从三个部分入手:

  • 模型,也称为抽象,软件核心部分,该系统与其它系统有所区别的关键
  • 接口,通过怎样方式将模型提供的能力暴露,是我们与这个软件交互的入口
  • 实现,就是软件提供的模型和接口在内部是如何实现的,是软件能力得以发挥的根基

了解设计的顺序: 模型=》接口=》实现。了解设计,需要一层一层地展开,在每个层次都按照模型、接口和实现进行理解,在头脑中形成一棵设计树。

了解设计,先模型,再接口,最后是实现。

0 人点赞