当前位置: 首页 > news >正文

《持续交付:发布可靠软件的系统方法》- 读书笔记(七)

持续交付:发布可靠软件的系统方法(七)

    • 第 7 章 提交阶段
      • 7.1 引言
      • 7.2 提交阶段的原则和实践
        • 7.2.1 提供快速有用的反馈
        • 7.2.2 何时令提交阶段失败
        • 7.2.3 精心对待提交阶段
        • 7.2.4 让开发人员也拥有所有权
        • 7.2.5 在超大项目团队中指定一个构建负责人
      • 7.3 提交阶段的结果
      • 7.4 提交测试套件的原则与实践
        • 7.4.1 避免用户界面
        • 7.4.2 使用依赖注入
        • 7.4.3 避免使用数据库
        • 7.4.4 在单元测试中避免异步
        • 7.4.5 使用测试替身
        • 7.4.6 最少化测试中的状态
        • 7.4.7 时间的伪装
        • 7.4.8 蛮力
      • 7.5 小结

第 7 章 提交阶段

7.1 引言

当更改项目状态(向版本控制库的一次提交)时,提交阶段就开始了。当它结束时,你要么得到失败报告,要么得到后续测试和发布阶段可用的二进制产物和可部署程序集,以及关于当前应用程序状态的报告。理想情况下,提交阶段的运行应该少于五分钟,一定不会超过十分钟。

从许多方面来看,提交阶段都是部署流水线的入口。它不但是候选发布版本的诞生地,也是很多团队实现部署流水线的起点。当团队使用持续集成时,就会创建这个流程中的提交阶段。

这是极其重要的第一步。提交阶段的使用能确保项目花费最少的时间做代码级别的集成。它能驱动一些好的设计实践,并且对代码质量和交付速度产生很大影响。

提交阶段也是应该开始构建部署流水线的起点。

在第3章和第5章中,我们已经简单地介绍了提交阶段,而本章将更详细地讨论如何创建有效的提交阶段和高效的提交测试。可能主要感兴趣的是开发人员,因为他们是从提交阶段得到反馈的主体。提交阶段如图7-1所示。
在这里插入图片描述
在这儿提示一下提交阶段是怎样工作的。当某人向版本控制库的主干上提交了一次变更后,持续集成服务器会发现这次变更,并将代码签出,执行一系列的任务,包括:

  • 编译(如果需要的话),并在集成后的源代码上运行提交测试;
  • 创建能部署在所有环境中的二进制包(如果使用需要编译的语言,则包括编译和组装);
  • 执行必要的分析,检查代码库的健康状况;
  • 创建部署流水线的后续阶段需要使用的其他产物(比如数据库迁移或测试数据)。

这些任务由持续集成服务器通过调用相应的构建脚本组织在一起。关于构建脚本化详见第6章。如果该阶段成功了,二进制包和结果报告就被保存在你的中央仓库中,以供交付团队和部署流水线的后续阶段使用。

对于开发人员来说,提交阶段是开发环节中最重要的一个反馈循环。它会为开发人员引入的最常见错误提供迅速反馈。提交阶段的结果是每个候选发布版本的生命周期中一个重大的事件。这一阶段的成功是唯一进入部署流水线,启动该软件交付流程的途径。

7.2 提交阶段的原则和实践

如果说部署流水线的目标之一是消除无法在生产环境运行的构建的话,那么提交阶段就是“门卫”。提交阶段的目标是在那些有问题的构建引起麻烦之前,就把它们拒之门外。提交阶段的首要目标是要么创建可部署的产物,要么快速失败并将失败原因通知给团队。

接下来,我们讨论建立高效提交阶段的原则和实践。

7.2.1 提供快速有用的反馈

提交测试的失败通常是由以下三个原因引起的:

  • (1) 由于语法错误导致编译失败;
  • (2) 由于语义错误导致一个或多个测试失败;
  • (3) 由于应用程序的配置或环境方面(包括操作系统本身)的问题引起。

无论是什么原因导致了失败,提交测试一结束,就要通知开发人员,并提供简明的失败原因报告,比如失败测试的列表、编译错误或其他错误清单。开发人员还应该可以很容易地拿到提交阶段运行时的控制台输出,即使提交阶段在多台机器上运行。

引入错误后,越早发现它,就越容易修复它。因为引入错误的人对其上下文的印象还比较深,而且找到错误原因的方法也比较简单。如果开发人员修改了一些内容并因此导致某个测试失败,而失败原因不是非常明显,最自然的做法就是查看从最后一次成功提交后到目前为止所有修改过的内容,来缩小搜查范围。

如果开发人员按照我们的建议,频繁提交修改的话,每次变更都会比较小。如果部署流水线能快速发现失败(最好是在提交阶段)的话,那变更的范围就仅限于该开发人员自己修改的代码。也就是说,修复那些在提交阶段发现的问题,要比修复那些由后续运行大量测试的阶段发现的问题简单得多。

提交阶段是第一个将质量视角从个体开发人员扩大到更多人的正式步骤。提交阶段的第一件事儿就是把提交者的修改与主线合并,然后对集成后的应用程序执行某种自动化的“验证”。既然“尽早识别错误”是我们的目标,那么就要做到“有问题就尽早失败”,所以提交阶段要捕获开发人员引入到应用程序中的大多数错误。

7.2.2 何时令提交阶段失败

关于“提交阶段只有成功和失败两种状态的限制是否太严格了”有很多争论。有人认为,在提交阶段结束时,应该提供更丰富的信息,比如关于代码覆盖率和其他度量项的一些图表。实际上,这些信息可以使用一系列阈值聚合成一个“交通灯信号”(红色、黄色、绿色),或者浮动的衡量标度。比如,当单元测试覆盖率低于60%就令提交阶段失败,但是如果它高于60%,低于80%的话,就令提交阶段成功通过,但显示成黄色。

可在现实中,我们从来没看到过这么复杂的东西,但曾经做过下面这样的事:写一个脚本,当某次构建的编译警告的数量比前一次增多或者没有减少时,就让提交阶段失败(这就是“渐进式”实践),如3.6.4节所述。当然,如果重复代码的数量超出了某个事先约定的限制,或者有关代码质量的其他度量项不符合约束条件时,就令提交阶段失败,这是完全可以接受的。

但要记住的是,我们的纪律是如果提交阶段失败,交付团队就要立即停下手上的工作,把它修复。如果全团队尚未就某个原因达成一致意见,就不要让提交测试失败,否则大家会不拿失败当回事儿,而持续集成就渐渐会失去其应有的作用。我们强烈建议在提交阶段持续检查应用程序的质量,并在恰当的时候考虑加强代码质量的度量。

7.2.3 精心对待提交阶段

提交阶段中有构建用的脚本和运行单元测试、静态分析等的脚本。这些脚本需要小心维护,就像对待应用程序的其他部分一样。和其他所有软件系统一样如果构建脚本设计得很差,还没得到很好维护的话,那么保持它能够正常工作所需投入的精力会呈指数级增长。这相当于双重打击。一个较差的构建系统不但会把昂贵的开发资源从创造业务功能的工作中拖走,而且会令那些仍在创建业务功能的开发人员的工作效率降低。我们曾经见过几个项目因严重的构建问题导致停工。

正如第6章所述,要确保将脚本做成模块化的。将那些经常使用但很少变化的常见任务与经常需要修改的任务(比如向代码库中增加模块)分开。将部署流水线中不同阶段所用的代码分别写在不同的脚本中。最重要的是,不要写出与具体环境相关的脚本,即要把具体环境配置与构建脚本分离。

7.2.4 让开发人员也拥有所有权

在某些组织中会有一支专家团队,团队成员都精通创建有效且模块化的构建流水线,并且擅长管理这些脚本的运行环境。本书的两位作者都曾经担当过这样的角色。但是,如果真的只有那些专家才有权维护持续集成系统的话,那就是一种失败的管理方式。

交付团队对提交阶段(也包括流水线基础设施的其他部分)拥有所有权是至关重要的,这与交付团队的工作和生产效率是紧密联系在一起的。如果你设置了人为障碍,使开发人员不能快速有效地作出修改,就会减缓他们的工作进程,并在其前进的道路上埋下地雷。

开发人员和运维人员都必须要习惯构建系统的维护工作,而且要对其负责。

7.2.5 在超大项目团队中指定一个构建负责人

在小团队或只有二三十人的团队中,自组织就可以了。如果构建失败了,通常很容易在这种规模的团队中确定谁(一位或多位负责人)该负责修复它,如果他没进行修复的话则提醒一下他,如果他在进行修复,就帮他一下。

但在大团队中,这并不总是一件容易的事。此时,让某个(或多个)人扮演构建负责人的角色是必要的。他们不但要监督和指导对构建的维护,而且还要鼓励和加强构建纪律。如果构建失败,构建负责人要知会当事人并礼貌地(如果时间太长的话,不礼貌也没问题)提醒他们为团队修复失败的构建,否则就将他们的修改回滚。这个角色能起作用的另一种情况是,当团队刚开始接触持续集成时。在这样的团队中,构建纪律还没有建立起来,有个人能不断提醒大家,会令事情走向正轨。

构建负责人不应该是由固定的人担任。团队成员应该轮流担当,比如每星期轮换一次。这个纪律不错,能让每个人都学到一些经验。无论怎么说,想一直做这项工作的人还是不多的。

7.3 提交阶段的结果

与部署流水线的所有阶段一样,提交阶段既有输入,也有输出。输入是源代码,输出是二进制包和报告。产生的报告包括测试结果(假如测试失败,这些结果是找出哪里出了错的重要信息)和代码库的分析报告。分析报告可能包括测试覆盖率、圈复杂度、复制/粘贴分析、输入和输出耦合度以及其他有助于建立健康代码库的度量项。提交阶段生成的二进制包应该在该部署流水线的实例中一直被重用,而且(如果可能)最后还会发布给用户。

制品库
提交阶段的输出(结果报告和二进制包)需要保存在某个地方,以便部署流水线的后续阶段能重用它们,并使团队也能使用它们。最容易想到的地方就是版本控制库,但它却不是一个正确的选择,因为这会让你的硬盘空间很快被吃掉,而且有些版本控制系统对二进制文件支持不佳。

绝大多数时新的持续集成服务器都会提供一个制品库,还能设置保存多长时间之后就自动删除那些不想要的二进制包。它们一般会提供某种机制让你声明需要保留任意任务运行后生成的哪些二进制包,并提供一个Web接口来方便团队获取结果报告和二进制包。当然,你也可以使用一个专用制品库(比如像Nexus或Maven风格的仓库管理器)来处理二进制包,但这些工具通常不适合于结果报告的保存。仓库管理器使我们更容易从开发机器上访问到二进制包,而无需与持续集成服务器集成。

图7-2显示了一个制品库在典型安装中的使用方式。它是为每个候选发布版本保存二进制包、结果报告和元数据的关键资源。下面是一个候选发布版本在理想情况下在部署流水线中成功走向生产环境的每一步,其序号与图7-2中各阶段相对应。

  • (1) 交付团队的某个人提交了一次修改。
  • (2) 持续集成服务器运行提交阶段。
  • (3) 成功结束后,二进制包和所有报告和元数据都被保存到制品库中。
  • (4) 持续集成服务器从制品库中获取提交阶段生成的二进制包,并将其部署到一个类生产测试环境中。
  • (5) 持续集成服务器使用提交阶段生成的二进制包执行验收测试。
  • (6) 成功完成后,该候选发布版本被标记为“已成功通过验收测试”。
  • (7) 测试人员拿到已通过验收测试的所有构建的列表,并通过单击一个按钮将其部署到手工测试环境中。
  • (8) 测试人员执行手工测试。
  • (9) 一旦手工测试也通过了,测试人员会更新这个候选发布版本的状态,指示它已经通过手工测试了。
  • (10) 持续集成服务器从制品库中拿到通过验收测试(根据部署流水线的配置,也可能是手工测试)的最新候选发布版本,将其部署到生产测试环境。
  • (11) 对这个候选发布版本进行容量测试。
  • (12) 如果成功了,将这个候选版本的状态更新为“已通过容量测试”。
  • (13) 如果部署流水线中还有后续阶段的话,一直重复这种模式。
  • (14) 一旦这个候选发布版本通过了所有相关阶段,把它标记为“可以发布”,并且任何被授权的人都能将其发布,通常是由质量保证人员和运维人员共同批准。
  • (15) 一旦发布以后,将其标记为“已发布”。

在这里插入图片描述

为简单起见,我们用顺序方式来描述这一过程。对于前面的阶段,的确是按这种顺序方式进行的,它们也应该被顺序执行。然而,根据项目的不同,验收阶段的几个后续阶段不串行执行也是正常的。比如,手工测试和容量测试就可以被验收测试的成功同时触发。另外,测试团队还可以将不同版本的候选发布版本部署到他们的环境中。

7.4 提交测试套件的原则与实践

对于提交测试套件的管理,有一些重要的原则和实践。提交测试中,绝大部分应由单元测试组成,这也是本节中我们主要讲的内容。单元测试最重要的特点就是运行速度非常快。有时候,我们会因为测试套件运行不够快而令构建失败。第二个重要的特点是它们应覆盖代码库的大部分(经验表明一般为80%左右),让你有较大的信心,能够确定一旦它通过后,应用程序就能正常工作。当然,每个单元测试只测试应用程序的一小部分,而且无须启动应用程序。因此,根据定义,单元测试套件无法给你绝对信心说“应用程序可以工作”,而这正是部署流水线后续部分的任务。

设计能快速运行的提交测试并不总是那么简单的事情。下面我们会介绍几种策略,其中大部分都是为了达到一个共同的目标:将指定测试的范围最小化,并让它尽可能聚焦于系统的某个方面。尤其要注意的一点是,运行的单元测试不应该与文件系统、数据库、库文件、框架或外部系统等打交道。所有对这些方面的调用都应该用测试替身代替,比如模拟对象(mock)和桩等。

7.4.1 避免用户界面

然而,对于提交测试来说,我们建议根本不要通过用户界面进行测试。用户界面测试的困难来自两方面。首先,它会涉及很多组件或软件的多个层次。这样是容易出问题的,因为要花很多时间和精力去准备各种各样的组件或数据,才能让测试运行起来。其次,用户界面是提供给用户手工操作的,而手工操作的速度与计算机操作的运行速度相比,是相当慢的。

如果你的项目或所用技术可以避免这两点的话,那么通过用户界面创建单元级别的测试可能也是值得的。然而,根据我们的经验,用户界面测试经常出问题,通常最好由部署流水线的验收测试阶段处理。

7.4.2 使用依赖注入

依赖注入(或控制反转)是一种设计模式,用于描述如何从对象外部建立对象间的关系。显然,只有在使用面向对象语言时才能用上它。

假如我创建了一个类,叫Car。无论我什么时候创建Car的一个实例,都可以让创建自己的Engine。另外,我也可以设计Car,使得在创建Car的同时,它会强制我提供一个Engine类给它。后者就是依赖注入。这样就更灵活了,因为我可以创建Car,并提供不同类型的Engine,却不用改变Car的代码。我们甚至可以为Car创建一个特别的TestEngine,它专门在Car类被测试时模拟Engine。

这种技术不但是构建灵活的模块化软件的很好的方法,而且它还能让测试变得很容易,只需要测试必要的类,那些依赖包就不再是包袱了。

7.4.3 避免使用数据库

刚接触自动化测试的人常常写出一些需要与代码中的某一层进行交互的测试,并将结果写入数据库,然后再验证该结果的确被写到了数据库中。尽管这种方法简单,容易理解,但从其他方面来说,它不是一个很有效的方法。

首先,这种测试运行得非常慢。当想重复测试,或者连续运行几次相似的测试时,这种有状态的测试就是个障碍。其次,基础设施准备工作的复杂性令这种测试方法的建立和管理更加复杂。最后,如果从测试中很难消除数据库依赖的话,这也暗示着,你的代码在通过分层进行复杂性隔离方面做得不好。这也使得可测试性和CI在团队身上施加了一种微妙的压力,迫使其开发出更好的代码。

提交测试套件的这些单元测试根本不应该依赖于数据库。为了达到这一点,你就要把被测试的代码与其存储分离开来。这就要求对代码实现良好的分层,也需要使用像依赖注入这样的技术。实在做不到的话,也至少要使用内存数据库。

然而,在提交测试中,也应该有一两个非常简单的冒烟测试。这些测试应该是端到端的测试,并选自那些高价值的、常用功能的验收测试套件,用来证明应用程序可以真正运行起来。

7.4.4 在单元测试中避免异步

在单个测试用例中的异步行为会令系统很难测试。最简单的办法就是通过测试的切分来避免异步,这样就能做到:一个测试运行到异步点时,切分出来的另一个测试再开始执行。

比如,当系统需要发出一条消息,再根据这个消息作出反应,那么可以自己实现一个接口封装原生的消息发送机制。然后你可以利用一个简单的实现了消息接口的桩或者下一节讲的模拟技术,先在一个测试用例中验证这种调用与你所期望的相同。然后,再增加第二个测试,只要通过消息接口调用一下原来的那个调用点,验证一下消息处理程序(message handler)的行为就可以了。当然这也依赖于你的架构,有时候可能需要很多工作才能做到这一点。

我们建议尽量消除提交阶段测试中的异步测试。依赖于基础设施(比如消息机制或是数据库)的测试可以算做组件测试,而不是单元测试。更复杂、运行得更慢的组件测试应该是验收测试的一部分,而不应该属于提交阶段。

7.4.5 使用测试替身

理想的单元测试集中在很小且紧密相关的代码组件上,典型的就是单个类或一小组极其相关的类。

如果系统设计得比较好,每个类都比较小,并通过与其他类的交互完成其运行目的。这是良好封装设计的核心,即每个类都不对外暴露它是如何达到其目标的。问题是,在这种设计得比较好的模块化系统中,为了测试一个在关系网中心的某个类,可能需要对它周边的很多类进行冗长的设置。解决办法就是与其依赖类进行模拟交互。

为这种依赖代码打桩已有相当长且光辉的历史啦。我们在前面已经描述过依赖注入的使用,而且,在建议将Engine替换为TestEngine时,也提供了一个打桩的简单例子。

打桩是指利用模拟代码来代替原系统中的某个部分,并提供已封装好的响应。桩并不对外界作出响应。这是个极其有用且灵活的方法,可以用在任何软件层次上,从模拟被测试代码依赖的一个非常简单的类,到模拟一个完整的系统。

对于大型组件和子系统的模拟,我们倾向于使用桩技术。但是,对于模拟编程语言级的组件,我们建议少用桩技术,更推荐使用模拟技术。相对来说,模拟技术(mocking)稍微新一些。使用它的动机是希望广泛利用与桩类似的技术,而又不需要我们自己写很多桩代码。让计算机为我们自动生成这些桩,而不是自己写,这样不是更好吗?

模拟技术恰好做到了这一点。现在有几种模拟技术工具集,比如Mockito、Rhino、EasyMock、JMock、NMock和Mocha等。使用模拟技术,你就可以说:“给我构建一个对象,让它假装就是某某类型的一个类。”

7.4.6 最少化测试中的状态

理想情况下,单元测试应聚焦于断言系统的行为。然而,特别对于那些刚接触有
效测试设计的新手来说,常见的问题是测试中状态的不断增加。实际上问题包括两个
方面。首先,很容易想到的是,测试就是为系统中的某个组件提供一些输入信息,然
后得到一定的返回结果。所以在写测试时,你就会组织一下相关的数据结构,以便以
正确的形式提交输入信息,然后再把结果与你期望的进行比较。事实上,所有的测试
或多或少都是这种形式。问题是,如果处理不当的话,这个系统及其相关的测试会变
得越来越复杂。

7.4.7 时间的伪装

在一个要求快速运行的构建里,对于那些需要“确保一定的延时或等待”的行为来说,这一点尤其重要。这么做以后,我们就可以通过调整代码结构保证测试时的所有延迟时间为0,使测试执行够快。假如单元测试需要某种真正延时才能运行的话,你就应该重新考虑一下代码结构和测试设计,避免这种情况发生了。

这在我们自己的开发中已经根深蒂固了。甚至是,只要代码中需要使用时间,我们就会抽象对系统时间服务的请求,而不是直接在业务逻辑中调用它们。

7.4.8 蛮力

开发人员总是为最快的提交周期争论不休。然而,事实上,这要与在提交阶段识别最常见错误的能力平衡考虑。这是个只能通过不断试错才能找到的优化过程。有时候,运行速度稍慢一点儿的提交测试可能优于通过优化测试或减少发现的缺陷数来追求运行速度的提交测试。

有两招儿能加快测试套件的运行。首先,将它分成多个套件,在多台机器上并行执行这些套件。时新的持续集成服务器都有“构建网格”功能,直接支持这种做法。记住,计算能力是廉价的,而人力是昂贵的。及时得到反馈比准备几台服务器的成本要有价值得多。第二招儿就是,作为构建优化过程的一部分,将那些运行时间比较长且不经常失败的测试放到验收测试阶段运行。然而,需要注意的是,这会导致需要更长的时间才能知道这些测试是否失败了。

7.5 小结

提交测试应该聚焦于一点,即尽快地捕获那些因修改向系统中引入的最常见错误,并通知开发人员,以便他们能快速修复它们。提交阶段提供反馈的价值在于,对它的投入可以让系统高效且更快地工作。

提交阶段的创建(一个每次修改都会触发的自动化过程,它将构建二进制包、运行自动化测试,并生成有效的度量报告)是采纳持续集成实践的一个最小集。假如你遵循了由持续集成引入的其他实践,比如定期提交,以及一旦发现缺陷就尽快修复,那么提交阶段会让交付流程在质量和可靠性方面有相当大的进步。尽管它只是部署流水线的起点,但可以为你提供巨大的价值,比如可以马上知道谁在什么时候提交的修改让应用程序无法工作,并能够马上修复,令应用程序恢复工作。

相关文章:

《持续交付:发布可靠软件的系统方法》- 读书笔记(七)

持续交付:发布可靠软件的系统方法(七) 第 7 章 提交阶段7.1 引言7.2 提交阶段的原则和实践7.2.1 提供快速有用的反馈7.2.2 何时令提交阶段失败7.2.3 精心对待提交阶段7.2.4 让开发人员也拥有所有权7.2.5 在超大项目团队中指定一个构建负责人 …...

Go源码实现使用多线程并发下载大文件的功能

摘要:Go语言编码实现了使用多线程并发下载文件的功能。 1. 代码流程介绍 1. 获取系统的CPU核心数量,并将其作为线程数的参考值,并打印出来。 2. 定义要下载的文件的URL、线程数和输出文件名。 3. 使用getFileSize()函数获取文件大小&#xf…...

Python基础入门例程1-NP1 Hello World!

描述 将字符串 Hello World! 存储到变量str中,再使用print语句将其打印出来。 输入描述: 无 输出描述: 一行输出字符串Hello World! 解答: str "Hello World!" print(str) 解释说明: 赋值变量&…...

前端面试题10.23

解决的最复杂的前端问题,介绍一下 最复杂的前端问题之一是浏览器兼容性,不同浏览器对网页的渲染方式存在差异,需要针对不同浏览器做兼容性处理。此外,前端性能优化也是一个复杂的问题,需要综合考虑网页加载速度、渲染…...

DYC算法开发与测试(基于ModelBase实现)

ModelBase是经纬恒润开发的车辆仿真软件,包含两个大版本:动力学版本、智能驾驶版本。动力学版包含高精度动力学模型,能很好地复现车辆在实际道路中运行的各种状态变化,可用于乘用车、商用车动力底盘系统算法开发、控制器仿真测试&…...

第四章 路由基础

目录 4.1 路由器概述 4.1.1 路由器定义 4.1.2 路由器工作原理 4.1.3 路由表的生成方式 (1)直连路由 (2)静态路由 (3)动态路由 4.1.4 路由器的接口 (1)配置接口 &#xff0…...

Java逻辑运算符(、||和!),Java关系运算符

逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式,以判断程序中的表达式是否成立,判断的结果是 true 或 false。 逻辑运算符是对布尔型变量进行运算,其结果也是布尔型,具体如表 1 所示。 表 1 逻辑运算符的用…...

三个设备文件

...

Java赋值运算符(=)

赋值运算符是指为变量或常量指定数值的符号。赋值运算符的符号为“”,它是双目运算符,左边的操作数必须是变量,不能是常量或表达式。 其语法格式如下所示: 变量名称表达式内容 在 Java 语言中,“变量名称”和“表达式…...

提升药店效率:山海鲸医药零售大屏的成功案例

在医药行业中,特别是医药零售领域,高效的药品管理和客户服务至关重要。随着科技的飞速发展,数字化解决方案已经成为提高医药零售管控效率的有效工具之一。其中,医药零售管控大屏作为一种强大的工具,正在以独特的方式改…...

使用Fragement(碎片)

一、Fragment简介 屏幕大小的差距可能会使同样的界面在不同设备上显示出不同的效果,为了能同时兼顾到手机和平板电脑的开发,从Android3.0版本开始提供了Fragment。 Fragment(碎片)是一种嵌入在Activity中的UI片段,它…...

种花问题(Python题目)

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。 给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 …...

STM32入门F4

学习资料:杨桃电子,官网:洋桃电子 | 杜洋工作室 www.doyoung.net 嵌入式开发核心宗旨:以最适合的性能、功能、成本来完成最有性价比的产品开发。 1.为什么要学F407 STM32F103系列与STM32F407系列对照表: 2.F4系列命…...

【30】c++设计模式——>状态模式

状态模式概述 状态模式是一种行为型设计模式,它可以让一个对象在其内部状态发生变化时更改其行为。通过将每个状态封装成一个独立的类,我们可以使状态之间互相独立,并且使得状态的切换变得更加灵活、可扩展。(多个状态之间可以相…...

中文编程开发语言编程实际案例:程序控制灯电路以及桌球台球室用这个程序计时计费

中文编程开发语言编程实际案例:程序控制灯电路以及桌球台球室用这个程序计时计费 上图为:程序控制的硬件设备电路图 上图为:程序控制灯的开关软件截图,适用范围比如:台球厅桌球室的计时计费管理,计时的时候…...

【python】高斯日记

题目: """ 题目描述: 高斯出生于1777年4月30日,记作1777-4-30。在此基础上,我们希望你写一个程序,给定一个数字n,表示从高斯出生的那天算起的第n天,输出这一天的具体日期。例如&…...

[论文笔记]MobileBERT

引言 今天带来一篇关于量化的论文MobileBERT,题目翻译过来是:一种适用于资源有限设备的紧凑型任务无关BERT模型。模型的简称是MobileBERT,意思是作者的这个BERT模型可以部署到手机端。 本篇工作,作者提出了MobileBERT用于压缩和加速BERT模型。与原始BERT一样,MobileBERT…...

【Spring Cloud】如何确定微服务项目的Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本

文章目录 1. 版本选择2. 用脚手架快速生成微服务的pom.xml3. 创建一个父工程4. 代码地址 本文描述如何确定微服务项目的Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本。 1. 版本选择 我们知道Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本选择一致性非常重…...

Power BI 傻瓜入门 1. 数据分析术语:Power BI风格

本章内容包括: 了解Power BI可以处理的不同类型的数据了解您的商业智能工具选项熟悉Power BI术语 数据无处不在。从你醒来的那一刻到你睡觉的时候,某个系统会代表你收集数据。即使在你睡觉的时候,也会产生与你生活的某些方面相关的数据。如…...

【C++和数据结构】位图和布隆过滤器

目录 一、位图 1、位图的概念 2、位图的实现 ①、基本结构 ②、set ③、reset: ④、test ⑤、问题: ⑥、位图优缺点及应用: ⑦、完整代码及测试 二、布隆过滤器 1、布隆过滤器的提出 2、布隆过滤器的实现 ①、基本结构 ②…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

EtherNet/IP转DeviceNet协议网关详解

一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...