(一)程序设计那些事儿:什么是程序设计?

设计

1. 开发层面的设计

设计,是开发过程中频繁探讨的重要议题。然而,“设计”一词所涵盖的范围极为广泛,涉及多个相似却又不尽相同的领域,诸如“设计模式”“系统设计”等。正因如此,在讨论设计时,常常会出现概念模糊的情况,难以明确具体所指。

狭义的“设计模式”,通常特指经典的GoF三大类及二十三小类设计模式。这些模式旨在解决特定场景下的问题,比如如何有效减少代码的重复性,以及如何降低模块之间的耦合度,从而提高代码的可维护性、可扩展性和可复用性。它们提供了通用的解决方案模板,并非简单的代码具体实现,而是一种抽象的、可复用的设计思路。重点在于,提高代码的可维护性、可扩展性和可复用性。

“系统设计-方案设计”则更侧重于一整个业务的落地实现。它关注的是如何将产品需求转化为可编程的模型,进而通过编写代码达成产品需求。如果是大的需求,初期需求就是系统设计。这一过程需要从整体上把握业务逻辑,基于需求抽象出不同的模块,并规划它们之间的交互方式,是对业务架构层面的考量;如果是小的需求,基于已有功能的,则是方案设计。这一过程需要对已有系统比较清楚,能够在现有架构下找到合适的切入点,设计出满足新需求的解决方案。重点在于,将产品需求转化为可编程可实现的模型,模型会由多个模块组成。

在完成具体的“系统设计”,确定了若干与业务紧密绑定的模块后,如何具体实现这些模块便成为新的问题,像undo - redo功能的实现、命令分发机制的构建等。为了与狭义的“设计模式”相区分,可将此类针对模块具体实现方式的设计称为“模块设计”。重点在于模块内部具体的实现和设计。

此外,还有“优化设计”,它主要聚焦于对现有系统或代码进行性能优化、资源优化等;

“开发规范设计”则着重制定统一的代码编写规范、团队协作流程规范等,确保开发过程的一致性和高效性。

清晰地区分这些概念,有助于我们从不同维度深入理解设计的内涵,明确在不同阶段的设计工作中,应当重点考虑哪些因素,从而提升设计的质量和效率。

1.1 设计模式

  • 定义:设计模式是针对软件系统中反复出现的特定问题而总结归纳出的通用解决方案,其核心目标在于增强系统的灵活性与可扩展性,同时提升系统的可修改性与可维护性。
  • 作用
    • 避免代码重复:在软件项目中,若大量重复代码散布于各处,一旦这些代码需要修改,就必须对多处进行改动,这不仅耗时费力,还极易引发错误,严重影响系统的扩展性。例如,在一个图形绘制系统中,如果多个图形绘制函数都包含相同的坐标转换代码,当坐标转换规则发生变化时,就需要逐一修改每个函数,增加了维护成本和出错风险。
    • 降低模块耦合度:过高的模块耦合度意味着一个模块的改动可能会对其他多个模块产生连锁反应,导致系统扩展性变差。例如,在一个电商系统中,如果商品展示模块与库存管理模块紧密耦合,当库存管理模块的业务逻辑发生变化时,可能会意外影响商品展示模块的正常运行。
    • 优化模块依赖关系:项目内部模块依赖关系混乱会给多人协作开发带来极大困扰。例如,当不同开发人员分别负责不同模块的开发时,模块间复杂的依赖关系可能使得代码提交过程变得繁琐,双方可能需要反复修改和提交代码才能完成合并,这本质上反映了系统在扩展性方面的不足。
  • 示例:经典的设计模式分为三大类共二十三小类,如单例模式、工厂模式、观察者模式等,此外还有一些类似 condition - action 这样针对特定场景的设计方式。

1.2 业务设计(系统设计/架构设计)

  • 定义:架构设计是对整个系统的高层次规划,涵盖系统的整体结构搭建以及各个功能模块的合理划分。其核心任务是将产品需求转化为可通过编程语言实现的模型,这一过程需要综合考虑系统的各个层面。
  • 作用
    • 满足业务需求:系统设计旨在运用系统化的方法构建和工程化系统,以全方位满足业务或组织的多样化需求。从底层基础设施的选型到数据存储方式的确定,都需纳入考量范畴。同时,要紧密结合业务需求,对不同功能模块进行精准划分。例如,在设计一个在线教育系统时,需要根据教学业务流程,划分出课程管理模块、学生管理模块、教师管理模块以及教学资源管理模块等。
    • 保障系统性能:通过对系统的深入分析,精准识别可能成为性能瓶颈的模块,并及时进行针对性调整。例如,对于一个日活跃用户量为 100k 的大型电商网站,其架构设计必然与日活跃用户量仅为 1k 的小型网站存在显著差异。若按照处理 100k 用户量的标准来设计面向 1k 用户量的网站,无疑会增加不必要的维护成本;反之,若以 1k 用户量的设计标准来应对 100k 用户量的业务需求,网站很可能频繁出现崩溃等性能问题。因此,在系统设计初期,就必须明确各个模块可能面临的性能挑战,并评估是否能够有效克服这些瓶颈。
  • 示例:以电商系统为例,其系统设计通常包含用户管理模块,负责处理用户注册、登录、信息管理等功能;商品管理模块,用于商品的上架、下架、库存管理等;订单处理模块,承担订单生成、支付处理、订单状态跟踪等任务;支付系统,保障支付流程的安全与顺畅;数据库表结构设计,合理规划数据存储方式;API 接口设计,实现不同模块之间以及与外部系统的交互。具体业务流程可能包括客户端发起购买请求,服务器端接收并处理请求,校验用户账户余额,确保支付安全,完成数据存储并保证事务性等操作。又如摇一摇、漂流瓶、扫码登录等功能,可设计为单独的微服务程序模块,每个模块具备完整独立的功能。

1.3 模块设计

  • 定义:模块设计聚焦于系统中具体模块的设计,着重探索某一特定功能具体实现的通用方法。它处于架构设计的子层级,关注的是针对具体模块甚至更细化功能的设计方式,强调对具体功能的抽象与提炼。
  • 作用:通过精心设计具体模块或功能的实现方式,提高模块的独立性,降低模块间的耦合度,从而提升模块的可维护性。这使得每个模块都能相对独立地进行开发、测试和维护,便于团队协作和系统的长期演进。
  • 示例:以 undo - redo 功能为例,模块设计需要考虑哪些操作需要支持 undo - redo,如何进行操作注册,采用何种方式存储操作记录,以及怎样实现操作回滚等。其他常见的模块设计示例还包括事件队列、命令分发器、消息中心、状态机等,它们各自针对特定的功能需求,提供了通用的设计思路和实现方法。

1.4 优化设计

  • 定义:优化设计是基于特定的先验假设,或者通过权衡利弊(trade - off)做出的优化改进措施。常见的优化方向是在适当增加系统复杂性的前提下,换取系统在某些关键指标上的提升,如性能、资源利用率等。
  • 作用:在特定的应用场景下,通过实施优化设计,显著提升系统性能,满足业务对系统性能的更高要求,提高系统的运行效率和响应速度。
  • 示例:例如,在某些场景下,如果能够确定 ABA 问题对系统影响不大,就可以采用 CAS(Compare - And - Swap)操作,通过无锁方式实现线程安全,从而避免传统锁机制带来的性能开销。又如 constexpr template metaprogramming(CTRP)技术,它将部分计算开销转移到编译时,减少运行时的计算负担,提高程序的运行效率。

1.5 开发规范设计

  • 定义:开发规范设计是通过制定静态检查规则以及形成团队共识,对软件开发过程中的代码编写、项目结构组织等方面进行规范。
  • 作用:规范代码编写方式,有助于提升代码质量,减少因代码风格不一致导致的潜在错误。同时,提高代码的可读性,使团队成员能够基于共同的规范快速理解代码意图,降低沟通成本,提高团队协作效率。
  • 示例:常见的开发规范设计包括制定命名规范,如变量命名、函数命名遵循特定的命名风格;规划工程组织方式,明确项目文件的目录结构和模块划分;区分对外函数和对内函数,规定哪些函数用于对外提供接口服务,哪些函数仅在模块内部使用等。

2. 其他设计

开发之外,还有若干与产品、运维、质量和安全相关的设计维度,同样影响软件的最终效果:

2.1 用户体验设计(User Experience Design, UX Design)

  • 角度:从产品角度出发,以用户为中心进行考量。
  • 定义:用户体验设计聚焦于用户在使用产品过程中的全方位体验,涵盖界面设计,即通过合理布局、色彩搭配、图形元素运用等,打造美观且易用的界面;交互设计,关注用户与产品之间的交互方式,如操作流程、反馈机制等,确保交互自然流畅;以及信息架构设计,对产品中的信息进行分类、组织和呈现,使用户能够快速定位和获取所需信息。
  • 作用:旨在提升产品的可用性,使用户能够高效、便捷地完成各项任务,从而显著提高用户对产品的满意度,增强产品的竞争力。
  • 示例:例如设计一款移动购物应用,通过简洁明了的界面布局,将商品分类、搜索框、购物车等常用功能置于显眼位置,使用户无需复杂操作就能轻松完成商品查找、购买等任务,极大提升购物体验。

2.2 测试设计(Test Design)

  • 角度:基于测试角度,以保障软件质量为核心目标。
  • 定义:测试设计主要围绕测试用例的精心编写、科学合理的测试策略制定以及合适测试工具的选用。测试用例编写需要全面覆盖软件的功能、性能、边界条件等各个方面;测试策略制定要根据项目特点和需求,确定采用何种测试方法(如黑盒测试、白盒测试、灰盒测试等)以及测试的优先级顺序;测试工具的选择则要考虑其功能是否满足项目测试需求、操作是否便捷以及与项目技术栈的兼容性等。
  • 作用:通过严谨的测试设计,能够有效发现软件中的缺陷和漏洞,确保软件的质量和可靠性,为软件的稳定运行提供有力保障。
  • 示例:对于一个 Web 应用项目,编写涵盖单元测试,对单个函数或模块进行测试,验证其功能正确性;集成测试,检测不同模块之间的接口和交互是否正常;以及端到端测试用例,模拟真实用户场景,从用户发起请求到最终得到响应的整个流程进行测试。同时,选用如 Jest(用于单元测试)、Selenium(用于端到端测试)等自动化测试工具,提高测试效率和准确性。

2.3 持续集成和持续部署设计(CI/CD Design)

  • 角度:站在运维角度,着眼于优化软件开发和交付流程。
  • 定义:CI/CD 设计着重于自动化构建、测试和部署流程的规划与实施。通过自动化构建,将开发代码自动编译、打包成可运行的软件包;自动化测试确保每次代码变更都经过全面的测试验证,及时发现潜在问题;自动化部署则将通过测试的软件包自动部署到相应的环境中,实现代码的持续交付。
  • 作用:极大地提高开发效率,减少人工干预带来的错误和延误,保证代码能够快速、稳定地交付到生产环境,使团队能够更敏捷地响应业务需求变化。
  • 示例:在一个基于 GitHub 的项目中,利用 GitHub Actions 配置自动化构建和部署流水线。当开发人员将代码推送到主分支时,GitHub Actions 自动触发构建流程,使用 Maven(假设项目是 Java 项目)进行编译和打包,接着运行预先编写好的单元测试和集成测试。若测试全部通过,则自动将生成的软件包部署到云服务器上,实现整个过程的自动化和持续化。

2.4 安全设计(Security Design)

  • 角度:从安全角度出发,尽管安全设计与开发紧密相关,但由于其专业性和独特性,通常将其与常规开发区分开来。
  • 定义:安全设计致力于保障系统的安全性,涵盖多个关键方面,如身份验证,通过多种方式(如用户名密码、指纹识别、面部识别等)确认用户身份;授权,根据用户身份和角色,精确控制其对系统资源的访问权限;数据加密,对敏感数据进行加密处理,防止数据在传输和存储过程中被窃取或篡改等。
  • 作用:有效保护系统和数据免受各种未经授权的访问、恶意攻击以及数据泄露等安全威胁,确保系统的稳定性和数据的保密性、完整性。
  • 示例:设计一个企业级应用系统的安全方案,采用多因素身份验证系统,用户登录时不仅需要提供用户名和密码,还需通过手机验证码或硬件令牌进行二次验证,大大增强身份验证的安全性,确保只有授权用户能够访问系统及其敏感数据。

99. ref

  1. https://github.com/checkcheckzz/system-design-interview
  2. https://github.com/InterviewReady/system-design-resources
  3. https://github.com/donnemartin/system-design-primer
  4. https://refactoringguru.cn/



    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • (三)内核那些事儿:CPU中断和信号
  • (二)内核那些事儿:程序启动到运行的完整过程
  • (一)内核那些事儿:从硬件抽象到系统服务的完整框架
  • (七)内核那些事儿:操作系统对网络包的处理
  • (五)内核那些事儿:系统和程序的交互