现代C++软件架构:从基础原理到实践指南
现代C++软件架构:从基础原理到实践指南
软件架构
软件架构的重要性和好架构的基本原理
为了使产品满足业务需求和性能、可维护性、可伸缩性等属性,你需要设计它的架构,并且最好尽早完成。
无论你是否有意识地努力去构建它,最终你都会得到某种架构。如果经过几个月甚至几年的开发,仍然希望软件保持较高的质量,那么需要尽早采取一些措施。如果不考虑架构,那么软件很可能永远不会达到要求的质量。
优秀软件架构需要避免两件事
1、软件腐朽。有时也称为软件侵蚀,发生在软件的实现决策与之前规划的架构不对应时。所有这些差异都应被视为技术债务。即使在完成了最初的工作并构思了特定的架构之后,也需要不断监控系统的发展方式,以及它是否仍然符合用户的需求,因为这些在软件的开发过程和整个生命周期中可能发生变化。
2、意外架构。未能跟踪开发是否遵循所选择的架构或未能对架构进行有意识的规划,往往会导致所谓的意外架构。即使在其他领域应用了最佳实践(如进行了测试或遵循了特定的开发文化),意外架构也可能发生。
优秀架构的基本原理
辨别架构好坏很重要,但这不是一件容易的事。识别反模式是它的一个重要方面,但要设计一个好的架构,首要必须满足交付软件的期望,包括功能性需求、解决方案的属性,以及各方面的约束。其中许多约束可以很容易地从架构上下文中衍生出来。
架构上下文
架构上下文是架构师在设计可靠的解决方案时需要考虑的内容,它包括来自相关方(stakeholder)以及业务和技术环境的需求、假设和约束。
相关方
相关方指所有与产品有关的人。这些人可以是客户、系统用户或者管理人员。沟通是每个架构师必须掌握的关键技能,正确地管理相关方的需求是满足他们的期望(并以他们想要的方式实现)的关键。
客户关心的可能是编写和运行软件的成本、软件提供的功能、软件的生命周期、上市时间以及解决方案的质量。
系统用户可以分为两组:最终用户和管理员。前者通常关心软件的易用性(usability)、用户体验和性能。对于后者,更重要的方面是用户管理、系统配置、安全性、系统备份和系统恢复。
最后,对于从事管理工作的相关方来说,重要的事情是保持较低的开发成本,实现业务目标,跟上开发进度,以及保持产品质量。
业务和技术环境
架构会受到公司业务的影响,关键因素包括从产品策划到上市的时间(Time-To-Market,TTM)、产品推出时间表、组织结构、人力的使用和对现有资产的投资。
技术环境,是指已经在公司中使用的技术以及那些不管出于何种原因需要成为解决方案的一部分的技术。
使用敏捷原则开发架构
本质上,敏捷性是迭代式和增量式的。这意味着在敏捷的架构方法中,不能选择一个大的前期设计。相反,应该提出一个小的、但仍然合理的前期设计。最好的情况是,使用决策日志说清楚每个决策的依据。这样,如果产品愿景发生了变化,架构就可以随之演进。为了支持频繁的版本发布,前期设计方案应该逐步更新。以这种方式设计的架构被称为演进式架构。
为了让团队保持敏捷性,应该考虑有效的工作方法,并且只考虑重要的事情。实现这些目标的一种好方法是使用领域驱动设计。
领域驱动设计关注的是如何改善业务和工程之间的沟通,让开发人员关注领域模型。基于这个模型的实现通常会使设计更容易理解,并随着模型的变化一起发展。
C++的哲学思想
C++的哲学思想可以概括为三条规则:
❑C++底层不应该基于任何其他语言(汇编语言除外)。
❑只为使用的东西付费(不需要为没有使用到的语言特性付费)。
❑以低成本提供高级抽象(更高的目标是零成本提供高级抽象)。
SOLID和DRY原则
在编写代码时,有许多原则需要记住。在编写面向对象的代码时,你应该熟悉抽象、封装、继承和多态性这四个基本概念。
SOLID是一组实践,它可以帮助你编写更简洁、更不容易出现bug的软件。SOLID是一个首字母缩写词,由它背后的五个概念各自的第一个单词的首字母组成:
❑单一责任原则(Single Responsibility Principle,SRP)。
❑开放封闭原则(Open-Closed Principle,OCP)。
❑里氏替换原则(Liskov Substitution Principle,LSP)。
❑接口隔离原则(Interface Segregation Principle,ISP)。
❑依赖倒置原则(Dependency Inversion Principle,DIP)。
单一职责原则
简而言之,单一责任原则(SRP)意味着每个代码单元应该只有一项职责。这意味着要编写只做一件事的函数,创建代表一种东西的类型,以及构建只关注一个方面的更高级别的组件。
这意味着,如果类管理某种类型的资源——例如文件句柄,那么它应该只做这件事,其他事情——例如文件解析,应交给其他类型去做。
开放封闭原则
开放封闭原则(OCP)意味着,代码对扩展操作开放,对修改操作关闭。对扩展操作开放,意味着我们可以很容易地扩展代码支持的类型。对修改操作关闭,意味着现有的代码不应该改变,因为这通常会导致系统的其他地方出现bug。
里氏替换原则
本质上,里氏替换原则(LSP)指出,如果函数可以使用指向基对象的指针或引用,那么它也可以使用指向其派生对象的指针或引用[插图]。
接口隔离原则
接口隔离原则(ISP)就是像它的名字所暗示的那样。其表述如下:
不应该强迫客户端依赖它不使用的方法。
依赖倒置原则
依赖倒置原则(DIP)可以用于解耦。本质上,这意味着高级模块不依赖于低级模块,两者都依赖于抽象。
C++允许用两种方法倒置类之间的依赖关系。第一种方法是常规的多态方法,第二种方法是使用模板。
DRY原则
DRY是don’t repeat yourself(别重复你自己)的缩写,这意味着应该避免代码重复,尽可能重用代码。
耦合和内聚
耦合
耦合衡量的是一个软件单元对其他单元的依赖程度。耦合度高的单元依赖许多其他单元,耦合度越低越好。
内聚
内聚衡量的是软件中单位元素的关联程度。在高内聚的系统中,同一模块中的组件所提供的功能是密切相关的,感觉就像这些组件是共生的一样。
参考:现代C++软件架构