浅谈一下编程思想(二)软件架构

2023-11-22 10:04:01 浏览数 (3)

前言

浅谈一下编程思想(一) - 掘金 前面写了编程范式与设计原则,这次写一下软件架构。

一、认识软件架构

所有的软件系统 都可以降解为策略和细节这两种主要元素

  1. 策略体现的是软件中所有的业务规则与操作过程,是系统的真正价值所在
  2. 而细节是指让操作系统的人、其它系统以及程序员们与策略进行交互,但是又不会影响到策略本身的行为。它们包括IO设备、数据库、web系统、服务器、框架、交互协议等。

软件架构师的目标是创建一种系统形态,该形态会以策略为最基本的元素,并让细节与策略脱离关系。 一个设计良好的架构在行为上对系统最重要的作用就是明确和显式的反应系统设计意图的行为,使其在架构层面上可见

What(软件架构是什么)

软件架构是一个高级别的设计概念,用于定义和描述软件系统的基本组织结构、组件、模块以及它们之间的关系和交互方式。它是一个关于如何构建和组织软件系统的整体蓝图,目的是确保系统的可靠性、可维护性、性能和安全性。

Why(为什么使用软件架构)

  • 组织与管理:架构提供了一个清晰的设计框架,使开发团队能够更好地理解和协作,管理和分配开发任务。
  • 可维护性:通过明确定义各个组件和它们之间的接口,软件架构使得系统更容易维护和修改,降低了维护成本。
  • 性能:架构可以帮助优化系统性能,确保满足用户需求并提供高效的响应时间。
  • 扩展性:软件架构允许系统在未来扩展和升级,以满足新的需求和功能要求,同时减少对现有系统的干扰。
  • 安全性:架构有助于设计和实施安全措施,以保护系统免受潜在的威胁和攻击。

How(如何实现软件架构)

  • 需求分析:首先,明确系统的功能需求、性能需求、安全需求和可维护性需求。这些需求将影响架构的设计和选择。
  • 选择架构模式:根据需求选择适合项目的架构模式,例如分层架构、微服务架构、单体架构等。每种模式都有其适用的场景和优缺点。
  • 设计组件和模块:设计系统的各个组件、模块和它们之间的接口。确保组件之间的交互清晰明了,并考虑到未来的扩展性。
  • 性能和安全性考虑:在架构设计中考虑性能和安全性方面的因素,确保系统在这两个方面满足要求。
  • 持续改进:架构设计不是一次性的工作,应该在整个开发周期中进行审查和改进。根据项目的演变和新需求,不断优化架构。
  • 实施和开发:基于架构设计开始实施和开发系统。在开发过程中,严格遵循架构指导原则,确保系统按照设计进行构建。

二、软件架构目标

USE CASES 用例

一个系统的架构必须能够支持其自身的设计意图。也就是说,如果某系统是一个购物车应用,那么该系统的架构就必须非常直观地支持这类应用可能会涉及的所有用例。该系统的主要用例会在其系统结构上明确可见。开发人员将不需要在系统中查找系统所应有的行为,因为这些行为在系统顶层作为主要元素已经是明确可见的了,这些元素会以类、函数或模块的形式在架构中占据明显位置,它们的名字也能够清晰地描述对应的功能。

OPERATION 运行

架构在支持系统运行方面扮演着更实际的角色。如果需要每秒要处理 100 000 个用户,该系统的架构就必须能支持这种级别的吞吐量和响应时间。同样的,如果某个系统要在毫秒级的时间内完成对大数据仓库的查询,那么该系统的架构也必须能支持这类操作。

DEPLOYMENT 部署

一个系统的架构在其部署的便捷性方面起到的作用也是非常大的。设计目标一定是实现“立刻部署”。一个设计良好的架构通常不会依赖于成堆的脚本与配置文件,也不需要用户手动创建一堆“有严格要求”的目录与文件。总而言之,一个设计良好的软件架构可以让系统在构建完成之后立刻就能部署。

LEAVING OPTIONS OPEN 保留可选项

一个设计良好的架构应该通过保留可选项的方式,让系统在任何情况下都能方便地做出必要的变更。

DECOUPLING LAYERS 按层解耦

从用例的角度来看,架构的目标是让系统结构支持其所需要的所有用例。但是问题恰恰是我们无法预知全部的用例。好在架构师应该还是知道整个系统的基本设计意图的。也就是说,架构师应该知道自己要设计的是一个购物车系统,或是运输清单系统,还是订单处理系统。所以架构师可以通过采用单一职责原则(SRP)和共同闭包原则(CCP),以及既定的系统设计意图来隔离那些变更原因不同的部分,集成变更原因相同的部分。

DECOUPLING USE CASES 用例的解耦

譬如说,添加新订单的用例与删除订单的用例在发生变更的原因上几乎肯定是不同的,而且发生变更的速率也不同。因此,我们按照用例来切分系统是非常自然的选择。与此同时,这些用例也是上述系统水平分层的一个个垂直切面。每个用例都会用到一些 UI、特定应用的业务逻辑、应用无关的业务逻辑以及数据库功能。因此,我们对系统水平切分成多个分层的同时,也要按用例将其切分成多个垂直切分。

DECOUPLING MODE 解耦的模式

如果不同面向之间的用例得到了良好的隔离,那么需要高吞吐量的用例就和需要低吞吐量的用例互相自然分开了。如果 UI 和数据库的部分能从业务逻辑分离出来,那么它们就可以运行在不同的服务器上。而且需要较大带宽的应用也可以在多个服务器上运行多个实例。

INDEPENDENT DEVELOP-ABILITY 开发的独立性

当系统组件之间被高度解耦之后,开发团队之间的干扰就大大减少了。譬如说,如果系统的业务逻辑与其 UI 无关,那么专注于 UI 开发的团队就不会对专注于业务逻辑开发的团队造成多大的影响。同样的,如果系统的各个用例之间相互隔离,那么专注于 addOrder 用例的团队就不太可能干扰到负责 deleteOrder 用例的团队。

DUPLICATION 重复

我们不喜欢重复的代码,当代码真的出现重复时,我们经常会感到作为一个专业人士’自己是有责任减少或消除这种重复的。 但是重复也存在着很多种情况。其中有些是真正的重复,在这种情况下,每个实例上发生的每项变更都必须同时应用到其所有的副本上。重复的情况中也有一些是假的,或者说这种重复只是表面性的。如果有两段看起来重复的代码,它们走的是不同的演进路径,也就是说它们有着不同的变更速率和变更缘由,那么这两段代码就不是真正的重复。等我们几年后再回过头来看,可能就会发现这两段代码是非常不一样的了。

DECOUPLING MODES(AGAIN) 再谈解耦模式

  • **源码层次:**我们可以控制源代码模块之间的依赖关系,以此来实现一个模块的变更不会导致其他模块也需要变更或重新编译。

在这种解耦模式下,系统所有的组件都会在同一个地址空间内执行,它们会通过简单的函数调用来进行彼此的交互。这类系统在运行时是作为一个执行文件被统一加载到计算机内存中的。人们经常把这种模式叫作单体结构。

  • **部署层次:**我们可以控制部署单元(譬如 jar 文件、DLL、共享库等)之间 的依赖关系,以此来实现一个模块的变更不会导致其他模块的重新构建和部署。

在这种模式下,大部分组件可能还是依然运行在同一个地址空间内,通过彼此的函数调用通信。但有一些别的组件可能会运行在同一个处理器下的其他进程内,使用跨进程通信,或者通过 socket 或共享内存进行通信。这里最重要的是,这些组件的解耦产生出许多可独立部署的单元,例如 jar 文件、Gem 文件和 DLL 等。

  • **服务层次:**我们可以将组件间的依赖关系降低到数据结构级别’然后仅通过网络数据包来进行通信。这样系统的每个执行单元在源码层和二进制层都会是一个独立的个体,它们的变更不会影响其他地方(例如常见的服务或微服务就都是如此的)。

三、小结

一个设计良好的架构应该允许一个系统从单体结构开始,以单一文件的形式部署,然后逐渐成长为一组相互独立的可部署单元,甚至是独立的服务或者微服务。最后还能随着情况的变化,允许系统逐渐回退到单体结构。

四、参考

文中部分节选《架构整洁之道》

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:cloud.tencent.com/developer/s…

0 人点赞