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

首先是模型,它是一个软件的核心部分,也有人称之为抽象。设计最关键的就是构建出模型。

而理解一个设计中的模型,可以帮助我们建立起对这个软件整体的认知。
比如:

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

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

其次是接口,它决定了软件通过怎样的方式,将模型提供的能力暴露出去。它是我们与这个软件交互的入口。

比如:

  • 一个程序库的接口就是它的 API,但对于同样的模型,每个人会设计出不同的 API,而不同的 API 有着不同的表达能力。比如:Google 的 Guava 对 JDK 的一些 API 重新做了封装,其目的就是简化开发,而很多优秀的做法后来又被 JDK 学了回去。
  • 一个工具软件一般会提供命令行接口,比如,每个程序员必备的基本技能——Unix 命令行工具就是典型的命令行接口。
  • 一个业务系统的接口,就是对外暴露的各种接口,比如,它提供的各种 REST API,也可能是提供了 RPC 给其它系统的调用。
    ……
    如果你想深入源码,去了解一个软件,接口是一个很好的指向标。你可以从一个接口进入到软件中,看它是怎样完成各种基本功能的。

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

例如:

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

    所以,做每一个技术决策都应该结合自己所开发应用的特点,并不存在一个通用的解决方案。

在实际的工作中,我发现许多人以为的设计其实是这里所讲的实现。我也知道,“实现”很重要,但是,它必须建立在模型和接口的基础之上。因为在一个系统的设计中,模型是最核心的部分。如果模型变了,这个软件便不再是这个软件了,而接口通常反映的就是模型。所以,模型和接口的稳定度都要比实现高,实现则是要随着软件发展而不断调整。很多人都知道 Redis 这个键值对存储性能非常好,他们学习 Redis 时,对其单线程模型印象深刻,因为它简单高效。但随着人们使用 Redis 的增多,对 Redis 有了进一步的需求。所以,从 6.0 开始,它开始支持多线程版本,以便于更好地满足人们的需求。但即便 Redis 改成了多线程,它还是那个 Redis,它的模型和接口还是一如既往,只是实现变了而已。

总结:

了解一个软件设计,可以从三个部分入手:模型、接口和实现。

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

了解设计的顺序应该是,先模型,再接口,最后是实现

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