从三本书规劝人单元测试
《人月神话》
《人月神话》(1974年首次出版)中部分内容非常过时,会让人根本不知道他在讲什么(比如Ada语言(一门编程语言)、OS/360(一个操作系统)、Model 75(一个计算机体系?)、TESTRAN(一个调试程序)等等),有些内容却会让你大喊“太对了,就是这样!”,“原来这么早就有结论了吗?!”等等。不过这种强烈的对比,也体现出那一部分,也就是会让你喊“太对了,就是这样!”的结论,在历史的长河中有着经久不衰的生命力。
人月神话在当下最经典的价值,是其论证了软件开发的主要困难是复杂度,而主要的复杂度来自于设计概念,次要的复杂度是实现概念。换句话说,最困难的事情是知道“该怎么做才正确” ,而不是“做出来”。而正是因为搞清楚该怎么做特别的困难,才导致软件开发的效率上不去、可靠性难以提升、软件越来越难理解。
注意,《人月神话》的作者本着矫枉程序员过于乐观的心态和行事风格,行文和论点都有些悲观主义。不过大家不用太在意,这只是这次分享的开始,我们所开发的世界会一点一点美好起来的,本次分享也会明朗起来~
接下来,从软件的组成和特点出发,论证这个论点。再回顾我们现有使用的开发模型,展开更多的观点和思考。
软件的组成
我们可以分成四个层次,组件的选择、组件的概念、流程的设计、具体的编码。
- 组件的选择。比如我们有一个前端服务,两个后端服务,两个后端服务各有一个MySQL数据库,共用一个Redis。虽然这里已经涉及到了组件的概念,但是这里想强调的是组件的数量和外部依赖的选择(比如MySQL、Redis、Nginx等等)
- 组件的概念。比如两个后端服务各自有不同的功能。
- 流程的设计。比如两个后端服务怎么相互通信,某个服务怎么做鉴权等等。
- 具体的编码,if、for、func、return这些细节的东西。
这其中每一个部分都可能崩坏,而其中任何一环崩坏,都会导致最终软件的失败。顶层组成部分比较少见失败,一般崩溃的原因就是外部依赖过多、选择了不合适的外部意外、调用链过于复杂等原因。其他几个部分崩快的原因,我们顺着软件的固有特点一起看。
软件的固有特点
《人月神话》的作者认为软件有“无法规避的内在特性:复杂度、一致性、可变性和不可见性”。我个人对于认为这个翻译会导致一些误解,更好的翻译可能是“琐碎的细节、不存在通用理论、可变性、不可见性”。
不存在通用的理论
这一点比较好理解。比如物理、数学,这些学科的研究学者相信宇宙不是无序的,必然存在通用的理论。物理学家想在夸克中、或者统一场论中,找到某种通用理论。数学家通过证明,得出更加简单的公式来解释问题。
但是软件工程师得不到这样的安慰,因为软件的复杂度来自于不断变化、来来去去的人。因此软件的复杂度无法下降。
可变性
这很好理解,我们现在开发的软件就依旧在不断地变化,因为我们的开发模型是增量开发模型,而不是瀑布模型或者其他。同时变化的还有客户需求、运行环境、企业目标、部门结构等等,这些都会导致软件的变化。
然而新功能的增加,并不像在原有的积木上堆积木一样简单。一个原本设计良好的程序,很容易在功能的不断添加中崩坏,究其原因,是因为原本的概念设计,并不能满足之后的变化。而面对变化,如果只是在原有的概念上做加法,不对重新梳理概念,则会造成概念的混乱。而一个不清晰、不自洽的概念设计,恰恰是让软件变得复杂的根本原因。
这一特点,正对应着软件中各个组件的概念,对具体功能的变化,也对应着流程的设计。
我们的程序在什么情况下因为变化而丑陋甚至崩坏? 停下来想一想。
不可见性
相比之下,建筑有建筑图,陆地和海洋有地图,电路有电路图。而软件没有任何空间上的特征。
(听众会有争议)我们常常使用的流程图、时序图、数据流图、状态流图等等的图来将我们的设计可视化。确实,这些图让我们可以更好地表达、研究我们的设计,但是设计本身的难度没有降低。
可能一开始的设计是一个清晰的,只有一个选择分支的线性流程。但是这个图并不可以真正反映程序。由于并发的存在,一个资源的状态转换并不那么简单。由此,我们需要真正的流程图来设计基本的逻辑,要使用时序图 / 状态流图来考虑并发,可能还需要数据流图来考虑数据的存储、获取、加工。对于数据库,我们有专门的**ERD图……**设计本身的复杂度并不因为这些图的发明而降低,这些图都只是软件的宏观而片面的描述,这些图只是为我们研究程序的设计提供了片面的表达工具,我们的设计依旧难以尽善尽美。
这也解释了,为什么有了这些设计,我们依旧可能将一个功能实现坏。首要原因是我们的设计不完美(就如上文所讲),不过还有一个次要原因,设计和编码之间还有gap。
这一特点,可能是最重要的一个特点,其对应着组件概念、流程设计,还有流程设计与具体编码之间的GAP。
我们的程序什么时候因为设计的复杂性而丑陋甚至崩坏? 停下来想一想。
琐碎的细节
虽然程序的基本结构是非常简单的,只有循环、判断、计算、函数调用等等结构,然而在具体的编码上,我们堆砌的都是不同的内容,这些内容让程序的复杂度以非线性的速度增加。
随着复杂度的上升,列举所有的可能性变得困难、函数调用变得复杂、新功能的添加变得困难;软件越来越像一个黑盒。
这一部分,正对应着具体的编码。
我们的程序什么时候因为具体的编码而丑陋甚至崩坏? 举例:比如一不小心写下的错误,if err != nil{ return nil};变量名起错;一个复杂的与或非等等。
可以看到软件的固有特点,就是围绕着复杂度、组件、流程、编码来的。软件主要困难:复杂度和软件的组成、软件的特点有着内在的联系。
开发模型
没有困难的工作,只有勇敢的打工人。我们的开发模型是怎么应对软件的种种固有特点的。
瀑布和增量
曾经软件工程在开发模型上走过歧途,人们过于自信自己的理智,低估项目的复杂度,将瀑布模型视为高质量软件开发的唯一标准。
瀑布模型
推迟实现,严格依次执行需求分析、设计、实现、测试、回归、发布、部署。
对整个项目使用瀑布模型,这意味着我们必须从一开始就确定好所有的需求、然后做好所有的设计、全部实现后再进行测试。
现在我们流行对于整个项目,我们使用增量模型
增量模型
每隔一段时间发布一个版本,在版本发布的时间间隔内做完整的需求设计、编码、测试、回归,最后的固定的时间节点发布多个新功能/修复。
对整个项目使用增量模型,意味着我们可以先做出一个只含有基本功能的程序,然后通过多次版本发布来不断完善功能,而每个版本都是一个可用的程序。
我们回顾软件的固有特点,会发现这两种开发模型都不可能是完美,增量模型纵容了可变性,导致开发过程首尾不能相顾;瀑布模型忽视了不可见性,在复杂场景下我们几乎不可能从一开始就考虑周全。
因此我们倾向于在不同的场景中使用不同的开发模型,我们目前的模式是,对整个项目使用增量模型,随着客户和市场去不断发布新的版本,而对于单个新功能,我们大多数情况下是瀑布模型,先设计好,再去开发和测试;部分比较大的功能,我们可能会拆分成多个小需求,对每个小需求使用瀑布模型。
瀑布单个需求的开发
在对单个需求使用瀑布模型的过程中,《人月神话》提出了一种经验性的进度安排:1/3计划、1/6编码、1/4构建单元测试、1/4构建系统测试。可见在整个需求的开发过程中,最重要的是测试,应占用1/2的时间;次重要的是计划,占用1/3的时间。但是得在这里提醒,1/2的测试时间,是构建**自动化测试**+调试,而不是在本地运行起来、打个断点、看一下输入输出,这仅仅是调试。
另一点,我们能不能跳过单元测试,直接集成测试?
——节选《人月神话》
(系统测试)使用经过调试的构件单元。尽管其并不是普遍的实际情况一一不过通常的看法是一一系统集成调试要求只能在每个部分都能正常运行之后开始。
实际工作中,存在着与上面看法不同的两种情况。一种是“合在一起尝试”的方法,这种方法似乎是基于这样的观点:除了构件单元上的bug之外,还存在系统 bug(如接口),将各个部分合拢得越早,系统bug就出现得越早。另一种观念则没有这么复杂:使用系统的各个部分进行相互测试,避免了大量测试辅助平台的搭建工作。这两种情况显然都是合理的,但经验显示,它们并不完全正确一一在系统测试中使用完好的、经过调试的构件,能比搭建测试平台和进行全面的构件单元测试节省更多的时间。
但即使如此,我们单个需求依旧很难高质量实现。单个需求依旧可能隐藏了大量的细节,导致我们无法一次性就面面俱到;或者即使实现了需求,但是实现地比较丑陋,不便于维护;或者实现了需求但是性能上不优。而这一切的根本原因是,设计太难了,我们不能一次就设计好。
但是,我们多设计几次,能不能设计好?
《重构》
《重构 改善既有代码的设计》这本书中提供了70多种重构的方法/准则/手法,我们不展开介绍重构的方法,我们介绍一下重构前前后后的一些事情。不过我们首先提出观点:通过严谨而敏捷的重构,我们可以在实现单个需求的过程运用快速迭代模型,又好又快地实现需求。 虽然刚刚说到,软件可以分为四个层次上的概念,重构也对应着这四个层次上的重构,但是我们这里主要讨论流程的设计和具体的编码(最后两个层次)的重构。
软件工程希望建立完美的需求与设计,按照既有的规划编写标准划一的代码,这是结构的美。快速迭代颠覆全知全能的神话,用近乎刀砍斧劈的方法解决问题,在混沌的循环往复中实现需求,这是解构的美。而Martin Fowler(《重构》的作者)用敏捷而又严谨的方法论演绎了重构的美。
事实上,我们永远无法在一开始就构建出完美的需求,设计出完美的程序,完美运用各种设计模式。
——节选《模型和XP》
在设计前期使用模式常常导致过度工程(over-engineering)
而重构开创性地揭开了软件开发的后结构主义,得以让设计模式在快速迭代的互联网时代发光发亮。
重构在秩序和混乱、敏捷和质量中找到了一种维持平衡的方式。
什么是重构
——名词解释
在不改变代码外在行为的前提下,对现有代码的内部结构进行调整,以提高软件的可理解性,降低其修改成本。
——动词解释
在不改变代码外在行为的前提下,使用一系列重构准则(手法),调整其结构。
具体而言,我们从 前提、行为、目的 出发,理解重构
前提
重构不能改变软件的功能,也不能让用户感受到程序的变化。用户可能是客户、产品经理,也可能是程序员。
与重构的前提相似的是性能优化,和重构一样,性能优化不能改变程序的行为(除了执行速度),只会改变其内部结构。
行为
从表象来看,重构就是整理代码;学术地讲,重构是使用一系列的方法论,来整理代码(《重构》大部分篇幅都是在介绍相关的70余种方法论)。
只有一个重点:重构不是盲目而随性的,重构是有方法有策略的。
目的
重构的目的是为了让软件更加好理解,降低其修改成本。
重构改进软件设计/不容易腐败
这一目的,着重于软件的未来,而不是软件的当下。当我们只是为了短期目的,或者没有完全理解整体设计之前,就贸然修改代码,或者是两个还行的设计累加,造成了一个不好的设计……总之,各种各样的原因导致代码的结构慢慢流失,“软件慢慢腐烂”。而重构正是对这一过程的补救。
在《人月神话》中,对软件腐烂提供了更加严谨的数据:
——节选《人月神话》
- 缺陷修复总会以固定(20%~50%)的几率引入新的bug。
- (在操作系统中)模块的总数随着版本号的增加呈线性增长,但是随着单个模块修改而会影响到的模块数呈指数增长。
总结:软件维护是增加混乱度的过程,即使是最熟练的软件维护工作,也只是放缓了系统退化到非稳态的进程,以至于必须要重新进行设计。
重构使软件更加容易理解
这里的理解,有两层含义,一层是让现有软件更加容易让未来的人(包括自己)理解。另一层是在重构的过程中,自己会越来越理解这一块的设计。软件由细枝末节的细节组成。而我们总是不可能将所有的细节都牢记于心,强如HW,也总是说“让我看看代码再说”。
另外,小范围的重构能让我们看到更大范围内的“东西”
——节选《重构》
一开始我所做的重构都像这样停留在细枝末节上。随着代码渐趋整洁,我发现自己可以看到一些以前看不到的设计层面的东西。如果不对代码做这些修改,也许我永远看不见它们,因为我的聪明才智不足以在脑子里把这一切都想象出来。Ralph Johnson(《设计模式:可复用面向对象软件的基础》的作者之一)把这种「早期重构」描述为「擦掉窗户上的污垢,使你看得更远」。研究代码时我发现,重构把我带到更高的理解层次上。如果没有重构,我达不到这种层次。
和重构的目的对立的是性能优化:性能优化往往使代码较难理解,但是为了得到所需的性能不得不这么做。而重构为了让代码好理解,可能会让程序的运行速度下降,但是不能降低到用户可察觉的程度(这样就破坏了前提,性能也是用户可察觉的一部分)。
重构和性能优化有些对立,却又常常结伴出现。有时候为了执行一个优化,我们常常会先进行重构,再进行优化;或者让两者同时执行。
重构助你找到bugs
随着重构帮我们深入理解代码的行为,我们也更加清楚自己所做的一些假设,从这个角度来说,不找到臭虫都难。
就像Kent Beck(JUnit的作者,《敏捷编程解析》的作者)经常形容自己的一句话:
我不是个伟大的程序员;我只是个有些一些优秀习惯的好程序员而已。
重构助你提高编程速度
——节选《重构》
听起来有点违反直觉。当我谈到重构,人们很容易看出它能够提高质量。改善设计、提升可读性、减少错误,这些都是提高质量。但这难道不会降低开发速度吗?
我强烈相信:良好设计是快速软件开发的根本。事实上拥有良好设计才可能达成快速的开发。如果没有良好设计,或许某- -段时间内你的进展迅速,但恶劣的设计很快就让你的速度慢下来。你会把时间花在调试上面,无法添加新功能。修改时间愈来愈长,因为你必须花愈来愈多的时间去理解系统、寻找重复代码。随着你给最初程序打上一个又一个的补丁(patch) ,新特性需要更多代码才能实现。真是个恶性循环。
良好设计是维持软件开发速度的根本。重构可以帮助你更快速地开发软件,因为它阻止系统腐败变质,它甚至还可以提高设计质量。
《人月神话》和《重构》,矛和盾
什么时候重构
一般而来,重构不需要进行排期,而应该随着开发随时进行。我们没道理为了重构而重构,而应该是因为想做别的什么事,而重构可以帮助我们把那些事做得更好。
从重构的目的中,发现我们一般会在以下几种情况进行重构:
- 刚刚写完代码的时候,对写下的代码不满意,所以进行重构。
- 编写新功能前,发现在现有的设计下编写新功能非常麻烦,所以先重构现有设计,再写新功能。
- Debug的时候,现在的设计不容易发现bug或者修复bug,则一并进行重构。
- 新人理解代码的时候,尝试进行重构,并将重构的结果交给熟悉项目的人Review。
什么时候不重构
- 如果现有的代码满是错误,根本不能正常运行,则应该直接重写。
- 如果项目已经临近最后期限,则应该避免重构。如果开发的最后,时间已经来不及了,则说明早该进行重构了。
——节选《重构》
Ward Cunningham(Wiki概念的发明者,设计模式和敏捷软件方法的先驱之一)对此有一个很好的看法。他把未完成的重构工作形容为「债务」。很多公司都需要借债来使自己更有效地运转。但是借债就得付利息,过于复杂的代码所造成的「维护和扩展的额外开销」就是利息。你可以承受一定程度的利息,但如果利息太高你就会被压垮。把债务管理好是很重要的,你应该随时通过重构来偿还一部分债务。
重构的前提
这一部分,作者原文意见讲的足够好了,因此我节选原文:
——节选《重构》
如果你想进行重构(refactoring),首要前提就是拥有一个可靠的测试环境。……我并不把这视为缺点。我发现,编写优良的测试程序,可以极大提高我的编程速度,即使不进行重构也一样如此。这让我很吃惊,也违反许多程序员的直觉,所以我有必要解释一下这个现象。
……
如果认真观察程序员把最多时间耗在哪里,你就会发现,编写代码其实只占非常小的一部分。有些时间用来决定下一步干什么,另一些时间花在设计上面,最多的时间则是用来调试(debug) 。我敢肯定每一位读者都还记得自己花在调试上面的无数个小时,无数次通宵达旦。每个程序员都能讲出「花一整天(甚至更多)时间只找出一只小小臭虫」的故事。修复错误通常是比较快的,但找出错误却是噩梦一场。当你修好一个错误,总是会有另一个错误出现,而且肯定要很久以后才会注意到它。彼时你又要花上大把时间去寻找它。
……
那时候我还着迷于增量式开发(incremntaldevelopment),所以我尝试在结束每次增量时,为每个 class添加测试。……做这些测试还是很烦人,因为每个测试都把结果输出到控制台 (conole),而我必须逐一检查它们。我是个很懒的人,我情愿当下努力工作以免除日后的工作。我意识到我其实完全不必自己盯着屏幕校验测试所得信息是否正确,我大可让计算机来帮我做这件事。我需要做的就是把我所期望的输出放进测试代码中,然后做一个比较就行了。于是我可以舒服地执行每个class 的测试函数,如果一切都没问题,屏幕上就只出现一个"OK"。现在,这些 classes 都变成「自我测试」了。
确保所有测试都完全自动化,让它们检查自己的测试结果。
……
注意到这一点后,我对测试的积极性更高了。我不再等待每次增量结束,只要写好一点功能,我就立即添加测试。每天我都会添加一些新功能,同时也添加相应的测试。那些日子里,我很少花一分钟以上的时间在调试上面。
……
当然,说服别人也这么做,并不容易。编写测试程序,意味要写很多额外代码。除非你确切体验到这种方法对编程速度的提升,否则自我测试就显不出它的意义。多人根本没学过如何编写测试程序,甚至根本没考虑过测试,这对于编写自我测试代码也很不利。如果需要手动运行测试,那更是令人烦闷欲呕;但如果可以自动运行,编写测试代码就真的很有趣。
《代码整洁之道》
我们讲到了,重构的目的包括让代码更容易理解,这恰好是《代码整洁之道》的主要内容。《代码整洁之道》以Tips的形式,阐述了如果让命名、函数、注释、对象、模块、错误处理等方方面面变得整洁清晰。这里无法罗列这些Tips,只能泛泛介绍一下,为什么要整洁,何为整洁。另外,《代码整洁之道》也提到了单元测试(这已经全文第三次提到单元测试了),我们不妨也看一看这本书如何教我们写测试。
书的PDF
为什么要整洁
沼泽
一言以蔽之:糟糕的代码让我们效率低下,而改进糟糕代码最好的时机就是当下。
这里的观点,与上面讲到的差不多。我们把重点放在勒布朗(LeBlanc)法则和破窗理论。
——节选《代码整洁之道》
20 世纪 80 年代末,有家公司写了个很流行的杀手应用,许多专业人士都买来用。然后,发布周期开始拉长。缺陷总是不能修复。装载时间越来越久,崩溃的几率也越来越大。至今我还记得自己在某天沮丧地关掉那个程序,从此再不用它。在那之后不久,该公司就关门大吉了。
20年后,我见到那家公司的一位早期雇员,问他当年发生了什么事。他的回答叫我愈发恐惧起来。原来,当时他们赶着推出产品,代码写得乱七八糟。特性越加越多,代码也越来越烂,最后再也没法管理这些代码了。是糟糕的代码毁了这家公司。
……我们有专用的词来形容这种事(被糟糕的代码困扰)沼泽……
你当然曾为糟糕的代码所困扰过。那么为什么要写糟糕的代码呢?是想快点完成吗?是要赶时间吗?有可能。或许你觉得自己要干好所需的时间不够;假使花时间清理代码,老板就会大发雷霆。或许你只是不耐烦再搞这套程序,期望早点结束。或许你看了看自己承诺要做的其他事,意识到得赶紧弄完手上的东西,好接着做下一件工作。这种事我们都干过。
我们都曾经瞟一眼自己亲手造成的混乱,决定弃之而不顾,走向新一天。我们都曾经看到自己的烂程序居然能运行,然后断言能运行的烂程序总比什么都没有强。我们都曾经说过有朝一日再回头清理。当然,在那些日子里,我们都没听过勒布朗(LeBlanc)法则:稍后等于永不(Later equals never)。
……
务实的 Dave Thomas 和Andy Hunt 从另一角度阐述了这种情况。他们提到破窗理论。窗户破损了的建筑让人觉得似乎无人照管。于是别人也再不关心。他们放任窗户继续破损。最终自己也参加破坏活动,在外墙上涂鸦,任垃圾堆积。一扇破损的窗户开辟了大厦走向倾颓的道路。
态度
一言以蔽之:代码变得糟糕,纯属我们的过错。我们应该用我们的专业性捍卫代码的整洁。
最下面的例子比较有意思。
——节选《代码整洁之道》
你是否遇到过某种严重到要花数个星期来做本来只需数小时即可完成的事的混乱状况?你是否见过本来只需做一行修改,结果却涉及上百个模块的情况?这种事太常见了。
怎么会发生这种事?理由多得很。我们抱怨需求变化背离了初期设计。我们哀叹进度太紧张,没法干好活。我们把问题归咎于那些愚蠢的经理、苛求的用户、没用的营销方式和那些电话消毒剂。不过,亲爱的呆伯特,我们是自作自受,我们太不专业了。
这话可不太中听。怎么会是自作自受呢?难道不关需求的事?难道不关进度的事?难道不关那些蠢经理和没用的营销手段的事?难道他们就不该负点责吗?
……多数经理想要知道实情,即便他们看起来不喜欢实情。…… 。他们(需求/产品经理)会奋力维护进度和需求;那是他们该干的。你则当以同等的热情维护代码。
再说明白些,假使你是位医生,病人请求你在给他做手术前别洗手,因为那会花太多时间,你会照办吗?本该是病人说了算;但医生却绝对应该拒绝遵从。为什么?因为医生比病人更了解疾病和感染的风险。医生如果按病人说的办,就是一种不专业的态度(甚至是犯罪)。
同理,程序员遵从不了解混乱风险的经理的意愿,也是不专业的做法。
注:原注:1847年1gnaz Semmclweis(伊纳兹・塞麦尔维斯)提出医生应洗手的建议时,遭到了反对,人们认为医生太忙,接诊时无暇洗手。
何为整洁
如果我们泛泛而谈何为整洁,那就太过空洞了;而如果就事论事,则不是我们这场分享可以容乃下的体量。在这里,我想分享三个伟大的观点
优雅且高效
——Bjarne Stroustrup,C++语言发明者,C++ Programming Language(中译版《C++程序设计语言》)一书作者。
我喜欢优雅和高效的代码。代码逻辑应当直截了当,,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省得引诱别人做没规矩的优化,搞出一堆混乱来。整洁的代码只做好一件事。
——作者评价
Bjarne 用了“优雅”词。说得好!我MacBook上的词典提供了如下定义:令人愉悦的精致和简单。注意对“愉悦”一词的强调。
Bjarne显然认为整洁的代码读起来令人愉悦。读这种代码,就像见到手工精美的音乐盒或者设计精良的汽车一般,让你会心一笑。
整洁基于测试(第四次提到测试)
——Dave Thomas,OTI公司创始人,Eclipse 战略教父
整洁的代码应可由作者之外的开发者阅读和增补。它应有单元测试和验收测试。 它使用有意义的命名。它只提供一种而非多种做一件事的途径。它只有尽量少的依赖关系,且要明确地定义和提供清晰、尽量少的 API。代码应通过其字面表达含义,因为不同的语言导致并非所有必需信息均可以通过代码自身清晰表达。
——作者评论
Dave将整洁系于测试之上!要在十年之前,这会让人大跌眼镜。但测试驱动开发(Test Driven Development)已在行业中造成了深远影响,成为基础规程之一。Dave 说得对。没有测试的代码不干净。不管它有多优雅,不管有多可读、多易理解,微乎测试,其不洁亦可知也。
童子军
光把代码写好可不够。必须时时保持代码整洁。我们都见过代码随时间流逝而腐坏。我们应当更积极地阻止腐坏的发生。
借用美国童子军一条简单的军规,应用到我们的专业领域:
让营地比你来时更干净。
如果每次签入时,代码都比签出时干净,那么代码就不会腐坏。清理并不一定要花多少功夫,也许只是改好一个变量名,拆分一个有点过长的函数,消除一点点重复代码,清理一个嵌套 if 语句。
你想要为一个代码随时间流逝而越变越好的项目工作吗?你还能相信有其他更专业的做法吗?难道持续改进不是专业性的内在组成部分吗?
单元测试
单元测试代码也必须保持简洁。
我们在修改现有代码的时候,不免会让测试失败,这个时候我们就需要同步地去修改测试代码。测试代码是随着生产代码一起演进的。
测试代码越脏,改动测试代码越难,我们开发和写新测试的时间就越少。最后,维护测试代码可能会比维护生产代码更加困难。
测试所能带来的一切好处,都基于一份可以维护的测试代码;如果测试代码不可维护,测试代码就会慢慢丧失其权威性,我们最终就会抛弃测试,整个项目来到不可维护的状态。
总结
我想我们不应该轻信任何权威,对于任何重要的观点,我们应该进行交叉论证,求证多方。而恰巧的是,所有的这些书,都在告诉我们,在软件开发中,慢即是快,欲速则不达。更巧的是,每本书都在自己的领域中,提到了单元测试/* 自动化测试*。
单元测试
单元测试不能用来保证系统没有问题,但是事实上,没有任何测试能保证系统没有问题(软件工程的基本理论:测试能证明错误的存在,而不能证明错误不存在),只不过单元测试是写起来最快、运行起来最快、调试起来最快的,效能最高的方式。
但同时,仅仅有单元测试是不够的。单元测试可以保证程序员编写的概念是正确的,却无法保证程序员的概念是正确的。
——节选《人月神话》
在编写任何代码之前,规格说明必须提交给外部测试小组,以详细地检查说明的完整性和明确性。如同Vyssotsky所说的,开发人员自己无法完成这项工作:“ 他们不会告诉你他们不懂。相反,他们乐于自己摸索出解决问题和澄清疑惑的办法。
而这些书中关于单元测试的内容都惊人地一致:单元测试是一个优秀的、具有生命力的程序必不可少的一部分。
- 测试可以通过缩短集成、Debug的时间,来极大地提高开发速度,且具有交互性的测试能更加高效地提高开发速度(与非交互性的测试相比,出自《人月神话》其他章节)。
- 软件的概念设计的最重要,但是有时候我们不能一次性设计好。但是如果有了自动化测试,我们可以按实现、测试、重构、优化的顺序,一步一步优化我们的程序。
- 所以,来一起写单测吧~!单测可以极快发掘琐碎细节中的错误、加速开发和Debug的速度、让我们放心地将不好的设计重构成优雅的设计,而不用担心潜在的错误,并以此一步步降低程序的复杂度,让我们做出更好的设计,写出更好的代码,付出更少的时间,加更少的班,睡更多的觉!
内在联系
《人月神话》
- 因为软件的固有特点:琐碎的细节,所以我们为了保证当下代码的正确性,也为了保证未来的修改不破坏现有的系统,我们要编写单元测试。
- 一个需求的开发中,理应有一半的时间用于测试。
- 组件经过单独的测试(单元测试),再执行集成测试,效率最高。
- 由于设计的复杂,或许我们不能一次就开发好新功能/需求(所以我们需要重构)
- 由于软件会缓慢地腐败,所以我们不得不隔一段时间,对某些部分进行重新设计(所以我们需要重构)
- 为了软件健康地发展,我们需要重构现有的功能,以面对变化(所以我们需要重构)
《重构》
- 我们需要自动化测试才能重构。
- 重构的目的是让代码更加整洁(所以我们需要代码整洁之道)
- 重构还有其他各种各样的好处,因此重构的提前:自动化测试,非常重要。
《代码整洁之道》
- 测试让软件富有生命力。(防止人月神话中的腐败)
- 测试也需要清晰整洁,易于维护。(对应人月神话中的复杂度)
警示
这些书警示了我们诸多事情,比如软件本身就会随着开发和修复而腐败;而不整洁的代码更是具有破窗效应,想象一下到处都充满了这种不整洁后,我们如何前进;测试是许多问题的良药,但是测试的所有好处都依赖和生产代码同样优秀的测试代码。
下一篇: Velut arbor aevo.
推荐阅读
-
反传销网8月30日发布:视频区块链里的骗子,币里的韭菜,杜子建骂人了!金融大V周召说区块链!——“一小帮骗子玩一大帮小白,被割韭菜,小白还轮流被割,割的就是你!” 什么区块链,统统是骗子 作者:周召(知乎金融领域大V,毕业于上海财经大学,目前任职上海某股权投资基金合伙人) 有人问我,区块链现在这么火,到底是不是骗局? 我的回答是: 是骗局。而且我并不是说数字货币是骗局,而是说所有搞区块链的都是骗局。 -01- 区块链是一种鸡肋技术 人类社会任何技术的发明应用,本质都是为了提高社会的生产效率。而所谓区块链技术本质不过是几种早已成熟的技术的大杂烩,冗余且十分低效,除了提高了洗钱和诈骗的效率以外,对人类社会的进步毫无贡献。 真正意义上的区块链得包含三个要素:分布式系统(包括记账和存储),无法篡改的数据结构,以及共识算法,三者互为基础和因果,就像三体世界一样。看上去挺让人不明觉厉的,而经过几年的瞎折腾,稍微懂点区块链的碰了几次壁后都已经渐渐明白区块链其实并没有什么卵用,区块链技术已经名存实亡,沦为了营销工具和传销组织的画皮。 因为符合上述定义的、以比特币为代表的原教旨区块链技术,是反效率的,从经济学角度来说,不但不是一种帕累托改进,甚至还可以说是一种帕累托倒退。 原教旨区块链技术的效率十分低下,因为要遍历所有节点,只能做非常轻量级的数据应用,一旦涉及到大量的数据传输与更新,区块链就瞎了。 一方面整条链交易速度会极慢,另一方面数据库容量极速膨胀,考虑到人手一份的存储机制,区块链其实是对存储资源和能源的一种极大的浪费。 这里还没有加上为了取得所谓的共识和挖矿消耗的巨大的能源,如果说区块链技术是屎,那么这波区块链投机浪潮可谓人类历史上最大规模的搅屎运动。 区块链也验证不了任何东西。 所谓的智能合约,即不智能,也非合约。我看有人还说,如果有了智能合约,就可以跟老板签一份放区块链上,如果明年销售业绩提升30%,就加薪10%,由于区块链不能篡改,不能抵赖,所以老板必须得执行,说得有板有眼,不懂行的愣一看,好像还真是那么回事。 但仔细一想,问题就来了。首先,在区块链上如何证明你真的达到了30%业绩提升?即便真的达到老板耍赖如何执行? 也就是说,如果区块链真这么厉害,要法院和仲裁干什么。 人类社会真正的符合成本效益原则的是代理制度。之前有人说要用区块链改造注册会计师行业,我不知道他准备怎么设计,我猜想他思路大概是这样的,首先肯定搞去中心化,让所有会计师到链上来,然后一个新人要成为注册会计师就要所有会计师同意并记录在链上。 那我就请问了,我每天上班累死累活,为什么还要花时间去验证一个跟我无关的的人的专业能力?最优做法当然是组织一个委员会,让专门的人来负责,这不就是现在注册会师协会干的事儿吗?区块链的逻辑相当于什么事情都要拿出来公投,这个绝对是扯淡的。 当然这么说都有点抬举区块链了,区块链技术本身根本没有判断是非能力,如果这么高级的人工智能,靠一个无脑分布式记账就能实现的话,我们早就进入共产主义社会了。 虽然EOS等数字货币采用了超级节点,通过再中心化的方式提高效率,有点行业协会的意思,是对区块链原教旨主义的一种修正,但是依然无法突破区块链技术最本质的局限性。有人说,私有链和联盟链是区块链技术的未来,也是扯淡,因为区块链技术没有未来。如果有,说明他是包装成区块链的伪区块链技术。 区块链所涉及的所有底层技术,不管是分布式数据库技术,加密技术,还是点对点传输技术等,基本都是早已存在没什么秘密可言的技术。 比特币系统最重要的特性是封闭性和自洽性,他验证不了任何系统自身以外产生的信息的真实性。 所谓系统自身产生的信息,就是数据库数据的变动信息,有价值的基本上有且只有交易信息。所以说比特币最初不过是中本聪一种炫技的产物,来证明自己对几种技术的掌握,你看我多牛逼,设计出了一个像三体一样的系统。因此,数字货币很有可能是区块链从始至终唯一的杀手应用。 比特币和区块链概念从诞生到今天已经快10年了,很多人说区块链技术在爆发的前夜,但这个前夜好像是不是有点过长了啊朋友,跟三体里的长夜有一拼啊。都说区块链技术像是90年代初的互联网,可是90年代初的互联网在十年发展后,已经出现了一大批伟大的公司,阿里巴巴在99年都成立了,区块链怎么除了币还是币呢? 正规的数字货币未来发展的形式无外乎几种,要么就是论坛币形式,或者类似股票的权益凭证等。问题是论坛币和股票之前,本来也都电子化了,区块链来了到底改变了什么呢? 所有想把TOKEN和应用场景结合起来的人最后都很痛苦,最后他们会发现区块链技术就是脱裤子放屁,自己辛苦搞半天,干嘛不自己作为中心关心门来收钱?最后这些人都产生了价值的虚无感,最终精神崩溃,只能发币疯狂收割韭菜,一边嘴里还说着我是个好人之类的奇怪的话。 因此,之前币圈链圈还泾渭分明,互相瞧不起,但这两年链圈逐渐坐不住了,想着是不是趁着泡沫没彻底破灭之前赶快收割一波,不然可能什么都捞不着了。 前段时间和一个名校毕业的链圈朋友瞎聊天,他说他们“致力于用区块链技术解决数字版权保护问题”,我就问他一个问题,你们如何保证你链的版权所有权声明是真实的,万一盗版者抢先一步把数据放在链上怎么办。他说他们的解决方案是连入国家数字版权保护中心的数据库进行验证…… 所以说区块链技术就是个鸡肋,研究到最后都会落入效率与真实性的黑洞,很多人一头扎进链圈后才发现,真正意义上的区块链技术,其实什么都干不了。 -02- 不是蠢就是坏的区块链媒体 空气币和区块链的造富神话,让区块链自媒体也开始迎风乱扭。一群群根本不知道区块链为何物的妖魔鬼怪纷纷进驻区块链自媒体战场,开始大放厥词胡编乱造。 任何东西,但凡只要和区块,链,分,分布式,记账,加密,验证,可追溯等等这些个关键词沾到哪怕一点点,这些所谓的区块链媒体人就会像狗闻到了屎了一样疯狂地把区块链概念往上套。 这让我想起曾经一度也是热闹非凡的物联网,我曾经去看过江苏一家号称要改变世界的“物联网”企业,过去一看是生产路由器的,我黑人问号脸,对方解释说没有路由器万物怎么互联,我觉得他说得好有道理,竟无言以对。 好,下面让我们进入奇葩共赏析时间,来看看区城链媒体经常有哪些危言耸听的奇谈怪论 区块链(分布式记账)的典型应用是*?? 正如前面所说,真正意义上的区块链分布式记账,不光包括“记”这个动作,还包括分布式存储和共识机制等。而*诞生远远早于区块链这个词的出现,勉强算是“分布式编辑”吧,就被很多区块链媒体拿来强行充当区块链技术应用的典范。 其实事实恰恰相反,*恰恰是去中心化失败的典范,现在如果没有精英和专业人士的编辑和维护,*早就没法看了。 区块链会促进社会分工?? 罗振宇好像就说过类似的话,虽然罗振宇说过很多没有逻辑的话,但这句话绝对是最没逻辑思维的。很多区块链自媒体也常常用这句话来忽悠老百姓,说分工代表效率提高社会进步,而区块链“无疑”会促进分工,他们的理由仅仅是分工和分布式记账都共用一个“分”字,就强行把他们扯到一起。 实际情况恰恰相反,区块链是逆分工的,区块链精神是号召所有人积极地参与到他不擅长也不想掺合的事情里面去。 区块链不能像上帝一样许诺他的子民死后上天国,只能给他们许诺你们是六度人脉中的第一级,我可以赚后面五级人的钱,你处于金字塔的顶端。
-
实时音频和视频技术的发展与应用-1.1 双重音频和视频 从架构上看,双人音视频系统相对简单明了。红点代表房间信令服务,房间信令服务的主要功能是管理房间信息,实现容量协商和上下行链路的质量调节,例如当下行信道发生拥塞时,上行线路的码率和分辨率会降低。 在传输信道层面,我们的策略是优先直连,在跨区域、跨运营商的情况下,我们会选择单中转或双中转信道,在策略上尽量保持直连和中转信道同时存在,当其中一个信道的质量不好时,系统会自动切断到另一个信道的流量。 1.2 多人音视频 多人视频通话的产品形态是整个房间不超过 50 人,大盘平均房间规模约为 4.x 人,房间内部最多满足一个大视频和三个小视频(四屏)。根据这一条件,我们在架构中采用了典型的 SFU 小房间设计。 上图中的红点代表房间信令服务,主要用于房间管理和状态信息同步。房间管理主要包括用户列表的管理,例如哪些用户打开了视频/音频,我看了谁,谁看了我,这些都是基于房间管理的信息,然后房间信令服务会将这些信息同步到媒体传输服务进行数据分发。 房间服务的另一个作用是房间级容量协商和质量控制,例如,房间里的每个人一开始都支持 H.265 编码,当某个时刻进来一个只支持 H.264 编码的用户时,房间里所有的上游主播就必须把 H.265 切成 H.264。还有一种情况是,房间里有一定比例的人下行链路信道质量较差,这会导致上行链路房间质量下降。 在传输层面,我们采用的是单层分布式媒体传输网络,大家都选择中转方式,不区分双人和多人,采用 Full-Mesh 传输机制将所有数据推送过去,比如一个节点上的人并不都看另外两个人的视频,但还是会将视频推送给他们。
-
腾讯视频直播 02-推流-美颜滤镜 同样,腾讯云提供了 setBeautyFilter 方法来设置美颜风格、磨皮程度、美白程度和泛红程度 //style 磨皮风格:0:平滑 1:自然 2:朦胧 //美容级别:0-9。值为 0 时关闭美颜效果。默认值:0,关闭美颜效果。 //美白级别:取值 0-9。值为 0 时,将关闭美白效果。默认值:0,关闭美白效果。 //ruddyLevel:取值范围为 0-9。值为 0 时关闭美白效果。默认值:0,关闭美白效果。 public boolean setBeautyFilter(int style, int beautyLevel, int whiteningLevel, int ruddyLevel);; public boolean setBeautyFilter(int style, int beautyLevel, int whiteningLevel, int ruddyLevel) 滤镜 setFilter 方法可以设置滤镜效果,滤镜本身是一个直方图文件。setSpecialRatio 方法可以设置滤镜的程度,从 0 到 1,越大滤镜效果越明显,默认值为 0.5。 Bitmap bitmap = BitmapUtils.decodeResource(getResources, R.drawable.langman); if (mLivePusher) if (mLivePusher ! = null) { mLivePusher.setFilter(bmp); } 控制摄像头 腾讯云 sdk 默认为前置摄像头(可以通过修改 TXLivePushConfig 的配置函数 setFrontCamera 来修改默认值),调用一次 switchCamera 就切换一次,注意切换摄像头前要确保 TXLivePushConfig 和 TXLivePusher 对象已经初始化。 mLivePushConfig.setFrontCamera(true); // 默认前置摄像头。 mLivePusher.switchCamera; //切换摄像头。 ⑦ 设置徽标水印 腾讯视频云目前支持两种设置水印的方式:一种是在流媒体 SDK 中设置水印,原理是在 SDK 中对视频进行编码前在画面中设置水印。另一种方式是在云端设置水印,即由云端解析视频并添加水印标识。 建议使用 SDK 添加水印,因为在云端添加水印会有问题。下面是添加水印的 SDK 介绍: //设置视频水印 mLivePushConfig.setWatermark(BitmapFactory.decodeResource(getResources,R.drawable.watermark), 10, 10); // 最后两个参数是视频的水印。 //最后两个参数是水印位置的 X 轴和 Y 轴坐标。 mLivePusher.setConfig(mLivePushConfig); 如果需要对水印图像的位置进行模型适配,则需要调用水印规范化接口。 /设置视频水印 mLivePushConfig.setWatermark(mBitmap, 0.02f, 0.05f, 0.2f); //参数为水印图像。 //参数包括水印图像的位图、水印位置的 X 轴坐标、水印位置的 Y 轴坐标和水印宽度。后三个参数的范围是 [0,1]。 // 最后两个参数是水印位置的 X 轴坐标和 Y 轴坐标。 mLivePusher.setConfig(mLivePushConfig); TXLivePushConfig 中的 setHardwareAcceleration 方法可以启用或禁用硬件编码。 if (mHWVideoEncode){ if (mLivePushConfig ! = null) { if (Build.VERSION.SDK_INT < 18){ Toast.makeText(getApplicationContext, "Hardware acceleration failed, current phone API level is too low (min 18)"、 Toast.LENGTH_SHORT).show; mHWVideoEncode = false; } } } } mLivePushConfig.setHardwareAcceleration(mHWVideoEncode ? TXLiveConstants.ENCODE_VIDEO_HARDWARE : TXLiveConstants.ENCODE_VIDEO_SOFTWARE); mLivePusher.setConfig(mLivePushConfig); // 如果您不确定何时启用硬件加速,建议将其设置为 ENCODE_VIDEO_AUTO。 // 默认情况下启用软件编码,但如果手机的 CPU 使用率超过 80% 或帧速率为 10,SDK 将自动切换到硬件编码。 ⑨ 后台推流 在常规模式下,一旦应用程序进入后台,摄像头捕捉数据的能力就会被 Android 禁用,这意味着 SDK 无法继续捕捉和编码音频和视频数据。如果我们什么都不做,故事就会按照下面的脚本发展: 阶段 1(背景剪切后 10 秒 ->)- CDN 无法将视频流传输给观众,因为没有数据,观众看到的是主帧。 阶段 2(10 秒-> 70 秒)--观众一方的播放器因无法接收到直播流而退出,房间里空无一人。 第 3 阶段(70 秒后)--服务器直接断开了推送流媒体的 RTMP 链接,主播需要重新打开直播才能继续。 主播可能只是短暂地接了一个紧急电话,但各云提供商的安全措施会迫使主播的直播提前结束。 1) 设置 setPauseFlag 在开始推流之前,使用 TXLivePushConfig 的 setPauseImg 接口设置一个等待图像,其含义建议为 "主播将暂时离开,稍后再回来"。
-
41 个下载免费 3D 模型的最佳网站-使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 17. Clara.io Clara.io 是一个创建 3D 内容的全球平台,也是一个培养新 3D 艺术家的社区。Clara.io 提供+100,000个免费的3D模型,包括OBJ,Blend,STL,FBX,DAE,Babylon.JS,Three.JS格式,用于 Clara.io,Unity 3D,Blender,Sketchup,Cinema 4D,3DS Max和Maya。 使用说明:免费,标准和专业帐户仅供个人使用,如果您需要将 clara.io 用于商业用途,请与销售团队联系。 18. 3DExport 3DExport是一个市场,您可以在其中购买和销售用于CG项目的3D模型,3D打印模型和纹理。它提供15 +不同的3D格式供下载,如3DS MAX(.max),Cinema4D(.c4d),Maya(.mb,.ma),Lightwave(.lwo),Softimage(.xsi),Wavefront OBJ(.obj),Autodesk FBX(.fbx)等。它还提供15种不同的语言! 使用说明:免费下载仅供个人和非商业用途。 19. 3D Warehouse 3D Warehouse是一个开放的库,允许用户共享和下载SketchUp 3D模型,用于建筑,设计,施工和娱乐!任何人都可以免费制作,修改和重新上传内容到3D仓库,您可以找到任何您能想到的东西,如家具,电子产品,室内产品等。 使用说明:3D Warehouse中的所有模型都是免费的,因此任何人都可以下载文件以用于SketchUp甚至其他软件,如AutoCAD,Revit和ArchiCAD。 20. CadNav.com CadNav是CGI平面设计师和CAD / CAM / CAE工程师的在线3D模型库,我们提供超过50000 +免费3D模型和CAD模型下载。在CadNav网站上,您可以下载高质量的多边形网格3D模型,3D CAD实体对象,纹理,Vray材料,3D作品,CAD图纸等。 使用说明:免费下载仅供个人和非商业用途。 21. All3dfree.net 就像网站名称一样,它提供免费的3D模型,还包括Vray材料,CAD块,2d和3d纹理集合,无需注册即可免费下载。它是不断更新的,因此您可以查找或请求3DS,MAX,C4D,skp,OBJ,FBX,MTL等格式的模型。 使用说明:所有资源均不允许用于商业用途,否则您将承担责任。 22. Hum3D 自2005年以来,Hum3D帮助来自3多个国家的80D艺术家节省3D建模时间,并制作逼真的3D模型,用于电影,视频游戏,AR应用程序和可视化。所有模型均由首席3D艺术家进行验证,他们检查其是否符合专业要求和最新的3D建模标准。 使用说明:免费下载仅供个人和非商业用途。 23. Artist-3D.com 艺术家-3D 库存的免费 3D 模型下载按通用类别排序。它为人体解剖学、汽车、家具、火箭、卫星等模型提供 AutoDesk 3DS Max 格式。您还可以在浏览他们的网站时找到教程和类似类型的建模。 使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 24. Free the models 就像本网站的标题一样,它为3d应用程序和3d游戏引擎提供免费的内容模型。您可以为您的任何项目找到许多有趣且有用的模型!它提供3ds,wavefront,bryce,poser,lightwave,md2和unity3d格式的模型。还有一个很棒的纹理集合,可以在您最喜欢的建模和渲染程序中使用。 使用说明:您从这里下载的所有内容都可以免费使用,除非它不能包含在另一个免费的网络或CD收藏中,也不能单独出售。否则,您可以在商业游戏,3D应用程序或渲染作品中使用它。您不必提供信用,但如果您这样做,那就太好了。 25. Resources.blogscopia 本网站由一家名为Scopia的公司创建。他们制作3D图像和视频,您可以找到许多为CGI工作的信息架构设计的模型,所有这些都可以在现实生活中使用。您可以免费下载它们,但是,如果您想一次下载它们,您可以支付 3 到 9 欧元。 使用说明:您可以免费下载模型部分的所有文件。每个压缩文件都包含您也可以在此处找到的许可证。基本上,您可以对文件执行任何操作。唯一的限制是不归属于Scopia的重新分发。 26.ambientCG 1000+公共领域PBR材料适合所有人!环境CG是使用许多不同的方法和资产类型创建的,例如照片纹理(PBR),贴花(PBR),图集(PBR),照片纹理(普通),物质存档(SBSAR),雕刻画笔,3D模型和地形。您可以在所有项目中*使用它们! 使用说明:在 ambientCG 上提供下载的所有 PBR 材料、画笔、照片和 3D 模型均根据知识共享 CC0 1.0 通用许可提供。您可以复制、修改、分发和执行作品,即使是出于商业目的,也无需征得许可。信用将不胜感激。 不要满足于平庸的大理石纹理 - 立即使用我们的免费PBR大理石纹理升级您的3D设计。 27.Pixar One Twenty Eight 这是一个提供官方动画行业经典纹理的网站:皮克斯,创建于 1993 年,该纹理库包括 128 个重复纹理,现在免费提供。 它包含您来到的纹理,包括砖块和动物毛皮。肯定会有一些你可以使用的东西。 使用说明:皮克斯动画工作室的《Pixar One Twenty Eight》根据知识共享署名4.0国际许可协议进行许可。即使出于商业目的,您也可以重新混合、调整和构建您的作品,只要您以相同的条款对新创作进行信用和许可。 访问数以千计的免费纹理并提升您的设计游戏 - 立即开始下载! 28. 3DXO 即使有近 620 个免费贴纸可供下载,3DXO 也不是最大的资源,但它的内容非常有用,不需要注册。无论是简单的墙壁或地板,还是一些奇怪的小东西,您都需要的纹理都可以在此网站上看到。 使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 29. 3DModelsCC0 3DModelsCC0 与其他产品的不同之处在于它包含超过 250+ 个高质量 3D 模型,并且本网站上的所有内容都是免费的,完全是公共领域!使用我们的模型时无需信用或归属! 使用说明:为每个人提供完全免费的公共领域内容。 30.Sketch up texture club Sketchup Texture Club是一个非营利性的教育和信息门户网站,由3D社区的图像促进协会管理,特别强调面向学生和建筑和室内设计专业人士的可视化和渲染技术,以及所有正在学习3D可视化的人。 使用说明:您无需支付版税或使用费。纹理可以免费下载和使用。不允许将纹理作为竞争产品出售或重新分发,即使图像被修改也是如此。 31. FlippedNormals FlippedNormal 是一个提供计算机图形和 3D 资产的市场,您可以找到许多用于雕刻、建模、纹理、概念艺术、3D 模型、游戏资产或课程的高级资产! 使用说明:使用权限可能因型号而异。因此,在下载文件之前,请仔细检查每个下载页面上的许可证和使用权限。 32. NASA 3D NASA 3D网站是一个在线门户,提供与太空和各种NASA任务相关的大量三维模型和模拟。该网站是用户友好的,并提供有关每个型号的详细信息。该网站允许用户探索和下载几种不同格式的模型,包括 OBJ、STL 和 FBX,只需单击下载按钮即可。 使用说明: 要下载模型,只需单击模型页面上的下载按钮并选择所需的格式。 33. 3DAGOGO (Astroprint) 3DAGOGO 是一个提供广泛 3D 模型的网站,包括角色、车辆和建筑物。3DAGOGO 的独特功能之一是它专注于适合 3D 打印的模型,使其成为希望创建物理原型或模型的设计师的绝佳资源。要使用 3DAGOGO,设计师只需在网站上搜索他们正在寻找的模型类型,然后下载 STL 格式的文件。 使用说明: 要使用 3DAGOGO,只需搜索所需的 3D 模型类型并下载 STL 格式的文件。根据需要自定义模型,并确保在将其用于商业目的之前检查使用权限。 34. FreeCAD FreeCAD是一款了不起的3D建模软件,可让您在计算机上创建令人难以置信的3D设计。该软件可免费下载和使用,它提供了广泛的工具和功能,可用于创建用于各种目的的3D模型。 该网站易于浏览,您可以找到开始使用FreeCAD的所有必要信息。此外,该网站还提供一系列教程和指南,可帮助您了解 3D 建模的来龙去脉。 使用说明: 要下载模型,请访问网站并从库中选择所需的模型。该网站还提供了一系列使用该软件的教程和指南。 35. Pinshape Pinshape是一个提供一系列3D打印模型的网站。网站上提供的型号质量很高,因此您可以确保您的最终印刷产品看起来很棒。该网站提供了广泛的模型,包括从家居用品到小雕像和珠宝的所有物品。 但这还不是Pinshape所能提供的全部!该网站还允许用户上传和共享自己的3D模型。这意味着您不仅可以下载出色的模型,还可以通过分享自己的设计为社区做出贡献。此外,Pinshape 提供了一系列自定义选项,因此您可以调整和调整模型以满足您的特定需求。 使用说明: 要下载模型,请在网站上创建一个帐户,搜索所需的模型,然后单击下载按钮。该网站还为每种型号提供了一系列定制选项。 36.Yeggi Yeggi 提供了大量免费的 3D 模型,您可以下载各种格式的模型,例如 STL、OBJ 和 FBX。该网站易于使用,您可以按关键字、类别或特定网站搜索模型。 Yeggi 对于任何寻找 3D 模型的人来说都是一个很好的资源。它提供了大量的模型集合,从日常物品到复杂的机械,以及介于两者之间的一切。该网站的收藏量在不断增长,每天都有新的型号增加。 使用说明: 要下载模型,请在网站上搜索所需的模型,然后单击下载按钮。该网站还提供指向托管模型的原始网站的链接。 37. Open3DModel 来自开放3D模型的图像 Open3DModel具有各种类别的模型,包括建筑,车辆和角色。无论您需要建筑物,汽车还是人的3D模型,都可以在此网站上找到。 该网站易于浏览,您可以按类别或关键字搜索模型。每个模型都附带预览图像和详细信息,例如文件格式、大小和多边形数量。此信息可以帮助您选择适合您需求的模型。 使用说明: 要下载模型,请访问网站,从库中选择所需的模型,然后单击下载按钮。 使用最好的 3D 资产管理工具简化您的 3D 制作流程。立即试用它们,将您的 3D 项目提升到一个新的水平! 38. 3DExport 对于那些为其 3D 设计项目寻找 3D 模型、纹理和其他资源的人来说,该平台是一个很好的资源。该网站有大量模型可供选择,包括 3D 打印对象、游戏资产等。用户可以按类别、文件格式或价格范围浏览,以找到适合其项目的完美资源。此外,3DExport 还提供一系列教程和其他 3D 资源,以帮助用户提高技能并创建更令人印象深刻的设计。 使用说明: 要使用 3DExport,只需创建一个帐户并浏览可用型号。您可以按类别、格式和价格进行搜索,以找到所需的型号。找到喜欢的模型后,只需下载它并开始在您的项目中使用它。 39.Blend Swap Blend Swap是一个社区驱动的市场,提供与Blender软件兼容的各种免费3D模型。该平台允许用户共享和下载模型、纹理和其他资产,以便在他们的项目中使用。 使用说明: 创建免费帐户后,您可以浏览社区上传的大量3D模型。当您找到要使用的一个时,只需下载它并将其导入您选择的 3D 软件即可。 40. 3DShook 3DShook 是一个高级 3D 模型市场,提供一系列用于建筑、游戏等各个行业的高质量模型。该平台提供基于订阅的模型,具有不同的定价计划,允许用户访问一系列模型。 使用说明: 注册免费帐户后,只需浏览3D模型库,选择您喜欢的模型,然后以您需要的格式下载它们。 41. Smithsonian X 3D 史密森尼 X 3D 对于正在寻找历史文物和文物的高质量 3D 模型的设计师来说,这是一个独特的资源。该平台提供了大量3D模型,这些模型是根据史密森尼博物馆和研究中心中的真实物体扫描创建的。 使用说明:
-
从三本书规劝人单元测试
-
从入门到精通 Unity:探索不同相机视角跟随技巧(包括一/第三人称视角,速来围观!)(^_^)
-
未曾有因缘经-尔时波斯匿王。国大夫人。出入行来。常使四人。名扇提罗(扇提罗者汉言石女无男女根故名石女)。最大筋力。令此四人。担皇后舆。皇后所乘。七宝辇舆。留在祇洹精舍门外。敕诸黄门。令守护之。黄门转令四扇提罗。守夫人舆。其身自往佛边听法。扇提罗等。各于舆下。睡眠不识。时有凶人。偷取夫人珍宝辇舆一摩尼珠。尔时黄门。暂出看舆。不见宝珠。心中惶怖。惧夫人责。问石女言。使汝守舆。何故偷珠各各答言。实不偷也。黄门大怒。鞭打石女。苦痛彻骨。时有一石女。自审不偷横受楚毒。奔走逃突入精舍中。称怨大唤众皆闻之莫知所由。 波斯匿王和皇后出宫的时候经常用四名力气很大的石女抬轿。皇后所乘的轿子有七宝装饰。这一次被留在世尊精舍的门外,让太监看守。太监就转命四名石女守着轿子,自己跟随听佛讲法。这四名石女睡着了,有人偷了轿于上的一颗摩尼宝珠,她们也不失道。这时太监不放心,出来察看。发现轿子上的宝珠没有了,心中十分害怕皇后斥责,便问石女说:让你们看护轿于,你们怎么监守自盗偷宝珠呢?这些石女都说:不是她们偷的。太监发了脾气,用鞭子抽打石女,痛彻骨隋。其中有一名石女觉得自己没有偷窃宝珠而被鞭打,便逃入世尊讲法精舍,大声喊冤。大家听了都不知道发生了什么事情。 佛语阿难。汝可出往彼黄门所。无令横鞭无过之人。何以故。此四石女者。乃是皇后前世之师。自无过罪。何以横鞭。自造后世恶业因缘。是时皇后。闻佛此语。即起恭敬。合掌白佛。如世尊说。四担舆石女。乃是皇后前世时师。迷意不解。惟愿世尊。说其因缘。令诸会众普得闻知。佛告皇后。唤石女来。于世尊前。验其虚实。皇后奉命。即遣黄门。摄之将来。时四石女。见佛叩头啼哭。长跪合掌。白世尊曰。实不偷珠。有何因缘。横罗此罪。鞭打楚痛。身体破坏。世尊告言。罪业因缘。自身所造。非父母为。非从天堕。人行善恶。受苦乐报。如响应声。贪现前利。心行邪谄。不知后世累劫受殃。夫恶从心生。反以自贼。如铁生垢。消毁其形。王叉手白佛。前后说法。皆有因缘。令四石女。先世本业有何因缘愿佛为说。开悟盲冥多所利益。众人蒙祐。 佛对阿难说:你去到太监那里让他不要鞭打没有过错的人,这四名石女是皇后前生的老师,没有犯什么错,为什么要鞭打她们呢?这会造作后世的恶业因缘。皇后听到佛说这话,于是恭敬起立,合掌问佛:世尊所说这四名抬轿石女是我前世的老师,我十分迷惑,请您说说其中因缘由来,让大家都能明了知晓。佛告诉皇后:你召唤石女进来,在我面前可以验证虚实。皇后就命令太监把四名石女传来。四名石女见佛后,叩头啼哭,顶礼世尊说:实在不是我们偷了宝珠,因为什么缘故而遭此痛苦,鞭打加身呢?世尊说道:罪业的因缘是自己所造,并不是父母所为,更不是从天而降。人们造作善恶之事,就会有苦乐报应,就如同喊话回声一样如影随形。为了贪得现前的利益而做奸邪欺骗的事情,却不知后世多生多劫会遭受苦报。恶从心生,却是自己的敌人,如同铁器生锈,腐蚀其形。波斯匿王向佛致礼问道:世间的事情都有前后因缘,这四名石女前生有什么因缘请佛说一说,开导我们这些无知之人,利益大众。 佛告王曰。欲闻者善。着心谛听。吾今说之。佛复惟曰。今我法中。有诸比丘。言行不同。心口相违。或为利养钱财饮食。或为名誉要集眷属。或有厌恶。王法役使出家为道。都无有心向三脱门。度三有苦。以不净心。贪受信施。不知后世。弥劫受殃。偿其宿债。为是等故。岂得不说。