版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1 12内容提要内容提要n软件的首要技术使命n程序员应有的高尚品质n编码规范的意义何在n编码中的技术细节n代码优化、重构和自测n程序员的知识库和工具箱软件的首要技术使命软件的首要技术使命3n软件的首要技术使命管理复杂度软件开发本质上就是不断地发掘整个问题域中,错综复杂、相互连接的所有概念细节。其困难来自很多方面:必须去面对复杂、无序的现实世界;精确而完整地识别出各种依赖关系与例外情况;设计出完全正确而不是大致正确的解决方案等等。即使人类能发明出一种与现实中亟待解决的问题有着相同术语的编程语言,但是人们要想清楚的认清现实世界到底如何运作仍有很多的挑战,因此编程仍会十分困难。当软件要解决更大规模的
2、现实问题时,现实实体之间的交互行为就变得更为复杂,这些转而又增加软件解决方案自身的难度。所有这些困难的根源都在于复复杂性杂性!管理复杂度管理复杂度复杂度并不是软件开发中的什么新特征。没有谁的大脑能容得下一个现代的计算机程序,也就是说,作为软件开发人员,我们不应该试着在同一时间把整个程序都塞进自己的大脑,而应该试着以某种方式去组织程序,以便能够在一个时刻可以专注于一个特定的部分。这么做的目的是尽量减少在任一时间所要考虑的程序量。在软件架构的层次上,可以通过把整个系统分解为多个子系统来降低问题的复杂度。人类更易于理解许多项简单的信息,而不是一项复杂的信息。所有软件设计技术的目标都是把复杂问题分解成
3、简单的部分。子系统间的相互依赖越少,你就越容易在同一时间里专注问题的一小部分。精心设计的对象关系使关注点相互分离,从而使你能在每个时刻只专注于一件事情。4理想的设计特征理想的设计特征当我解决问题的时候,我从来不考虑美感。我只想着如何才能解决它。但一旦解决了问题,如果解决方法不够优美的话,我就知道做错了。 R. Buckminster Fuller最小的复杂度做出简单且易于理解的设计易于维护时刻想着维护你代码的程序员可能提出的问题松散耦合让各组成部分关联最小,合理抽象、封装、信息隐藏紧密内聚每个子程序在其抽象层上只做一件事情,但要做好可重用性本系统的某些组成部分能被其他系统重复使用高扇入复用率很
4、高的低层子程序低扇出不要过多调用其它子程序,降低同其它子程序的关联精简性只做需求范围内必须的事情,满足了上述诸项设计特征,自然会形成易扩展的代码。5信息隐藏的实例信息隐藏的实例6假设你有一个程序,其中每个结构体都通过一个名为id的成员变量来保存一种唯一的ID。一种设计方法是用一个整数来表示ID,同时用一个名为g_maxId的全局变量来保存目前已分配的ID的最大值。每当创建新结构时,你只要简单的使用id = +g_maxId这条语句,就肯定能获得一个唯一的ID值,这种做法会让创建结构体时的代码量最少。可这样设计可能有问题吗?好多地方都可能会出错。如果你想把某些范围的ID留做它用该怎么办?如果你想
5、使用非连续ID来提高安全性又该怎么办?如果你想重新使用已销毁对象的ID呢?如果你想增加一个断言来确保所分配的ID值不会超过预期的最大范围呢?如果程序中到处都是id = +g_maxId这种语句的话,一旦上面说的任何一种情况出现,你就需要修改所有这些语句。另外,如果你的程序是多线程的话,这种方法也不是线程安全的。创建新ID的方法就是一种你应该隐藏起来的设计决策。如果你在程序中到处使用+g_maxId的话,你就暴露了创建新ID的方法,也就是通过简单的递增g_maxId。相反,如果你在程序中都是用语句id = NewId(),那就把创建新ID的方法隐藏起来了。7你可以在NewId()子程序中仍然只用
6、一行代码,return +g_maxId,或者其他与之等价的方法。但如果日后你想把某些范围的ID留做它用,或者重用旧的ID时,只要对NewId()子程序的内部加以改动即可,无需改动几十个甚至成百个id = NewId()语句。无论NewId()内部做了多么复杂的改动,这些改动都不会影响到程序的其它部分。现在再假设你发现需要把ID的类型由整数改为字符串。如果你已经在程序内部大量地使用了int id这样的变量声明的话,那么即使改用NewId()子程序也无济于事。你还得深入到程序内部,进行几十次甚至几百次的修改。因此,另一个需要隐藏的秘密就是ID的类型。对外界透露ID是个整型变量的做法,实质上是在鼓
7、励程序员们对ID使用针对整数的操作,如、=等等。在C语言里,你可以简单的使用typedef来把ID定义为IdType一个可以解释为int的用户自定义类型而避免将其直接定义成int类型。再强调一下,隐藏设计决策对于减少“改动所影响的代码量”而言是至关重要的。 信息隐藏提及的秘密有两种类型:信息隐藏提及的秘密有两种类型:1. 隐藏复杂度2. 隐藏变化源信息隐藏的实例信息隐藏的实例程序员应有的高尚品质程序员应有的高尚品质n谦虚大部分编程工作都旨在弥补我们有限的智力。精通编程的人是那些了解自己头脑有多大局限性的人,都很谦虚。n求知欲 做试验 对编程和开发过程做试验,是学习编程的有效途径之一。如果不了解
8、所用语言的某一特性是怎么回事,可编写一个小程序来检验,看看它是如何工作的。用个小程序来检验某一概念,总比编写大程序时运用不太了解的特性要好。 阅读解决问题的有关方法 学习成功项目的开发经验 阅读文档8n诚实 不是高手时不假装是高手 乐于承认错误 提供实际的状况报告 力图理解编译器的警告,而非弃之不理 对编译器给出的消息不懂装懂是一个常见的盲点。如果你不明白某个编译器警告,或者自认为已经搞懂了但时间紧迫无法核实,猜猜时间实际浪费到哪儿了?最终你可能为了解决问题而从头检查一遍,而编译器早已把解决方案给出来了。9程序员应有的高尚品质程序员应有的高尚品质10程序员应有的高尚品质程序员应有的高尚品质n诚
9、实 透彻理解自己的程序,而不要只是编译看看能否运行 有些时候,当你对自己的程序并不很了解时,就“先编译看看能否运行再说”。一个例子是对于该用“”还是“=”,通过运行程序来决定。这种情况下,程序能否运行并没有太大意义,因为你并不理解它能工作的原因。要知道,测试只能找出错误,不能确保“不存在错误”。要是不理解程序,就不能彻底测试。“先编译看看程序会干什么”的想法是个警告信号,也许意味着你该回到规划阶段,或者你在着手编程时还不清楚要做什么。请确保在将程序丢给编译器之前,你已对其有了较大的把握。编码规范的意义何在编码规范的意义何在n保证代码的可读性计算机不关心你的代码是否好读。它更善于读二进制指令,而
10、非高级语言的代码。编写可读性好的代码,是为了便于别人看懂。可读性对程序的以下方面都有正面影响: 可理解性 容易复查 错误率 调试 可修改性 开发时间 外在质量11 在编程的早些年,程序被看作是程序员的个人财产。未经邀请,人们是不会读同事代码的(就像不会看别人情书一样)。而程序本质上是程序员写给硬件的情书,充满了只有配偶之间才知晓的细节。因此,程序里尽是些昵称和口语表达法,只有处于热恋的、以为世界只有他们两个的爱侣之间才能看懂。这样的程序对于外界来说简直就是天书。Michael Marcotty可读的代码写起来并不比含糊的代码多花时间,运行时至少不比后者慢。如果你能轻松阅读自己写代码,确保该代码
11、能工作也会更容易,这就是应该写可读性好的代码的充分理由。不仅如此,代码在复审过程中也要阅读它;你或别人修改错误时也要读;改动代码时还要读;当别人利用你代码的一部分编写类似的程序时也要读。使代码可读性好并非是开发过程中的可有可无部分,为了节省编写代码的时间而不顾阅读它的时间,是不经济的。你应该努力写好代码,这可以一次做到的,这远比你努力去读坏代码划算,因为那不得不一次次的反复做。12编码规范的意义何在编码规范的意义何在n规范是一套用于管理复杂度的智力工具许多编程细节都有一定程度的随意性。循环体中该缩进几个空格?怎样格式化注释?如何排列子程序?这些问题多数都有若干种正确答案。每次回答同样内容比起只
12、是回答正确更重要。规范能够节省程序员回答同一问题的麻烦应该总是重复使用同样的方案。当许多程序员工作于一个项目时,使用规范能避免各程序员随意决定导致的理解困难。规范可以使你免除各种风险。通过建立禁止使用危险做法的规范,可以在需要用它们时限制这些做法,或者防范它们可能的危险。例如,通过禁止全局变量或在一行里写多条语句,避免这些危险的用法。13编码规范的意义何在编码规范的意义何在规范增加了对低层工作的可预见性。对存储器请求、错误、输入/输出和系统接口有规范的处理,能够为代码加入有意义的结构,便于其他程序员弄懂你的程序只要他们知道你的规范。这样,你和你的读者就有了更好的默契,就能减少必须消化的细节数量
13、,反过来又进一步加深了对程序的理解。规范能够弥补语言的不足之处。对于不支持具名常量的语言(例如Python、Perl、Linux的Shell脚本等),规范可以区分用来读写的变量和用于只读的常量。限用全局变量和指针的规范是以规范来弥补语言缺点的另一个例子。14编码规范的意义何在编码规范的意义何在编码中的技术细节编码中的技术细节15n头文件的构成和布局1. 头文件的起止标记,以header_file.h文件为例/ 头文件说明性注释可选#ifndef HEADER_FILE_H_#define HEADER_FILE_H_空行空行#endif空行162. 头文件的四大组成部分 必须的其它头文件声明
14、包含后面三个部分所依赖的文件(先写系统头文件,再写自定义头文件)。若不是本头文件中必须用到的其它头文件,则不应包含在内,尤其不要为了贪图C源文件中编码的方便,罗列一大堆无关的系统头文件。切记!程序组织的逻辑性和合理性才是最重要的。包含系统头文件时,应把所用到的信息简要的注释出来。 宏定义 宏名称使用大写词组加下划线的形式,宏参数则借用局部变量命名的规则。宏定义应尽可能的少,不要用宏定义新的类型,一定要使用typedef进行类型的定义,也不要用宏取代函数的功能。头文件的构成和布局头文件的构成和布局 数据类型定义 类型定义统一用typedef来实现,每个typedef只定义一个类型,不要同时定义多
15、个类型。程序中出现的整数型常量应尽可能归入枚举类型中定义,例如状态量、操作码、错误码等等。 定义结构体要注意:不要把结构当做杂物箱堆放数据,而要把密切相关的信息放在一起。不要定义巨型结构。结构体内成员的类型要仔细斟酌,互斥信息可考虑用联合体来替换。不要为了节省存储空间而随便定义紧缩结构,尤其是含有指针成员的情况下。只有在处理通信协议报文格式的时候,经过深思熟虑后方可采用紧缩结构,同时还要避免内存不对齐和大小字节序的问题。 全局函数声明 所有函数声明前都要显式的冠以extern。只声明供给外部其它文件或模块使用的全局性函数。17头文件的构成和布局头文件的构成和布局18变量的使用变量的使用n变量的
16、使用 1. 命名在正式编码规范文件形成之前,只要求采用一致的命名规则即可,但要注意同其它语法元素构成明显差异。不好的例子:x = x - xx;xxx = fido + SalesTax( fido );x = x + LateFee( x1, x ) + xxx;x = x + Interest( x1, x );改进后:balance = balance - lastPayment;monthlyTotal = newPurchases + SalesTax( newPurchases );balance = balance + LateFee( customerID, balance )
17、 + monthlyTotal;balance = balance + Interest( customerID, balance ); 2. 定义和初始化原则上要求变量在定义的同时进行初始化。唯一例外是对变量的第一次引用是以指针的形式传给某个函数,由该函数在内部对其赋值。如果是这样的情况,那么要在变量定义处用注释说明之。变量(包括常量)初始化时,如果右值是常数或常数表达式,那么要保证右值的结果类型完全等价于左值的类型,不允许任何形式的类型转换发生。结构或联合变量初始化时,必须用GNU的C扩展语法,对必须的成员逐一显式的初始化,此时可保证其它未初始化的成员均为0。不要按照成员在结构体内出现的次
18、序,隐式的进行初始化。例如:typedef structchar* name;unsigned age; Student_t;19变量的使用变量的使用则显式定义变量tom为:Student_t tom = .name = “Tom Eddiegin”, .age = 25;而不要隐式的定义成:Student_t tom = “Tom Eddiegin”, 25;203. 作用域和生存期使变量引用局部化那些界于同一变量的多个引用点之间的代码可称为“攻击窗口”。可能会有新代码加到这种窗口中,不当的修改了这个变量,或者阅读代码的人可能会忘记该变量应有的值。一般而言,把对一个变量的引用局部化,即把引用
19、点尽可能集中在一起总是一种很好的做法。当把变量的引用点靠在一起的时候,也就使得代码的阅读者能每次只关注于一部分代码。而如果这些引用点之间的距离非常远,那你就要迫使阅读者的目光在程序里跳来跳去。因此,把变量的引用点集中起来的主要好处是提高程序的可读性。尽可能缩短变量的“存活”时间变量的存活时间开始于引用它的第一条语句,结束于引用它的最后一条语句。与作用域类似,保持较低的存活时间也是我们的目标,应使得对象的存活时间尽可能短,其好处也是减小攻击窗口。在你真正想要修改一个变量的那些位置之间的区域,该变量被错误或无意修改的可能性就降低了。变量的使用变量的使用214. 绑定时间对程序维护和更改有很深远影响
20、的一个话题就是“绑定时间”:把变量和它的值绑定在一起的时间。这一绑定是发生在编写代码的时候还是在编译程序时?是在程序加载的时候还是运行的时候?抑或其他时间?采用越晚的绑定时间会具有越多的灵活性。下面的代码演示了可能的最早绑定时间,即编写代码时:titleBar.color = 0 xff;/ 0 xff is hex value for color blue由于0 xff是硬编码(hard-coded)在程序里的数值,在编写代码的时候它就会被绑定到titleBar.color变量上。这种硬编码技术通常总是很糟糕的,因为一旦要修改这个0 xff,那么这个新值就无法同代码中其他那些必须和它一样的0
21、 xff值保持一致了。下面是代码编译时绑定的例子:static const int COLOR_BLUE = 0 xff;static const int TITLE_BAR_COLOR = COLOR_BLUE;titleBar.color = TITLE_BAR_COLOR;变量的使用变量的使用22变量的使用变量的使用 TITLE_BAR_COLOR是一个具名常量(也可使用枚举值替代),编译器会在编译的时候把它替换为一个数值,这种方法总要好于硬编码。由于TITLE_BAR_COLOR比0 xff更能反映出所代表的信息,因此增加了可读性。它也使得修改标题栏颜色变得更容易,因为一处改动就能对所
22、有位置生效。下面是一个晚期绑定的例子,即在运行期绑定: titleBar.color = ReadTitleBarColor(); ReadTitleBarColor()是一个能在程序运行期间读入数值的子程序,数值来源可能是个外部配置文件。与硬编码相比,晚期绑定更具灵活性。无需通过修改程序来改变titleBar.color,只要简单修改外部数据文件的内容即可。一般而言,绑定时间越早灵活性就越差,但复杂度也会越低。就前两种方案而言,使用具名常量要好于神秘数值(magic number),只要养成了这样的编程习惯,你就可以享受具名常量所带来的灵活性。除此之外,希望获得的灵活性越强,那么支持这种灵活
23、性的代码就会越复杂,出错几率也会越高。由于成功的软件开发需要依赖于将代码的复杂程度降到最低,因此一个熟练的程序员会按照需要引入足够的灵活性来满足需求,但是却不会增加需求范围之外的任何灵活性以及相应的复杂度。n基本数据类型1. 整数整数在使用中需要注意两大问题:除0和溢出。例如下面的代码:int div = 3;int fact = 4;int len = 7;fact = len / (div / fact);div / fact所得中间结果是0,会导致len / 0发生除0的异常。一种可能的修改方案是这样:fact = len * fact / div。这是个简单的例子,因为算术表达式的各项
24、都来自于变量的定值,更复杂的情况可能会来自于其它函数的调用结果、用户的命令行参数、外部的输入文件、甚至是网络报文中的某个字段。另一个常见的错误是溢出。避免整数溢出的最简单办法是考虑清楚算术表达式中的每个项,设想每项可能达到的最大值。例如,如果在整数表达式m=j*k中,j可预期的最大值是200,k可预期的最大值是25,那么m可预期的最大值就是200*25=5000。基本数据类型基本数据类型这在32位计算机上是没问题的,因为最大的整数是2147483647。然而,如果j可预期的最大值是200000,k可预期的最大值是100000,那么m可预期的最大值就是200000* 100000=2000000
25、0000。这时就行不通了,你就必须使用64位整数或者浮点数,以容纳m的预期最大取值。2. 浮点数使用浮点数时主要考虑的是,很多十进制小数不能够精确的用数字计算机中的1和0来表示。像1/3或者1/7这样的无限循环小数通常只用7位或者15位精度有效数字表示。这对于大多数用途而言是足够精确的,但是有时它的不精确性也足以给你带来麻烦。下面是两条在使用浮点数时应该遵循的指导原则。- 避免数量级相差巨大的数之间的加减运算避免数量级相差巨大的数之间的加减运算32位浮点变量,1000000.00+0.1可能会得到1000000.00,因为32位不能给你足够的有效位数包容1000000和0.1之间的数值区间。与
26、之类似,5000000.02-5000000.01很可能会得到0.0。如果你必须要把一系列差异如此巨大的数相加,那么就先对这些数排序,然后从最小值开始把它们加起来。这样做并不能消除舍入问题,但是能使这一问题的影响减少到最低限度。基本数据类型基本数据类型- 避免等量判断避免等量判断很多应该相等的浮点数值并一定相等。举例来说,10个0.1加起来很少会等于1.0。下面例子显示了应该相等但却不等的两个变量。double nominal = 1.0;double sum = 0.0;for (int i=0; i和=以及和=c) 合理选择if子句和else子句的判断逻辑,避免出现空的if子句d) 利用布
27、尔函数调用简化复杂的逻辑检测执行逻辑执行逻辑例如,给字符分类的代码可能会使用如下的检测串:if (inputCharacter SPACE) characterType = CharacterType_ControlCharacter;else if inputCharacter = | inputCharacter = ,| inputCharacter = .| inputCharacter = !| inputCharacter = :) characterType = CharacterType_Punctuation;else if (0 = inputCharacter &
28、 inputCharacter = 9) characterType = CharacterType_Digit;条件分支语句条件分支语句上面例子中的代码难读的一个原因是对字符分类的判断过于复杂。为了提高可读性,你可以把它替换成布尔函数调用。下面就是使用布尔函数取代判断后的代码示例:if ( IsControl( inputCharacter ) ) characterType = CharacterType_ControlCharacter;else if ( IsPunctuation( inputCharacter ) ) characterType = CharacterType_Pu
29、nctuation;else if ( IsDigit( inputCharacter ) ) characterType = CharacterType_Digit;else if ( IsLetter( inputCharacter ) ) characterType = CharacterType_Letter;条件分支语句条件分支语句e)确保所有的情况都考虑到了写一个放在最后的else子句,用出错消息或者断言来捕捉那些你不考虑的情况。这种消息是给你而不是给最终用户看的,因此请适当的措辞。f)尽可能用switchcase替换冗长的ifelse ifelse if. (GNU对C有扩展)g
30、)switchcase中,为case选择最有效的排列顺序如果你的case语句很长例如,一条用于处理事件驱动程序里面的数十个事件,或ioctl到驱动内部的成百上千个command handler那么case的顺序就很重要了。如果所有情况的重要性都相同,那么可以按A-B-C的字母顺序排列各个case,以便提高可读性,还很容易从中找出某个特定的情况来。如果有一个正常的情况和多个异常情况,那么就把那个正常情况放在最前面,异常情况可按异常的严重程度或可能出现的频度来排列。把最经常执行的情况放在最前面,最不常执行的放在最后。这样做,阅读程序的人可以很容易地找到最常见的情况。h)把default子句只用于检
31、查真正的默认情况i)谨慎使用fall-through的case如果要故意忽略掉某个case下的break,使执行顺序能够穿越到下一个case,那么一定在穿越处使用下面的注释强调这一技巧:/ fall through条件分支语句条件分支语句3. 循环语句最常用的循环是 for 和 while,这样的编码习惯会常令我们主观上忽视了dowhile循环的作用,某些少见的情形下,它会使得代码更直观易懂。作为一项使用循环的原则,当遇到某件事至少要做一次的时候,请确保你是经过了一番深思熟虑后,有充分理由认为其它循环方式比dowhile更易实现,才决定使用的。请注意循环体的内聚性。仅凭循环可同时做两件事的这一
32、事实,是无法充分证明这两件事是应该放在一起做的。循环应该和子程序一样,每个循环只做一件事并且把它做好。如果用两个循环会导致效率低下,那么就把代码写成两个循环,并注明可以把它们合并起来以提高效率,然后等测量数据显示程序的这一部分性能低下的时候再去合并它们。不要为了终止循环而胡乱改动for循环的下标,而要根据终止的原因在判断分支中用break优雅的退出,这样就能真实的表达程序作者的意图。检查端点检查端点对于简单的循环,通常需要注意三种情况:开始的情况,任意选择的中间情况以及最终的情况。在你创建循环的时候,应在脑海里运行这三种循环情况,循环语句循环语句以确认该循环不会出现任何off-by-one错误
33、。如果有一些特殊情况是与第一次或最后一次的情况都不同,那么也要检查它们。如果循环中包含了复杂的计算,那么就拿出计算器来手动地检查这些计算是否正确。是否愿意执行这种检查,是高效程序员和低效程序员之间的一项关键差别。高效的程序员既会在脑海里进行模拟,也会手动的执行运算,因为他们知道这些手段将会有助于找出错误来。低效的程序员会随意地做一些试验,直到他们找到了一种看上去能工作的组合。如果某个循环没有按照想象的那样去工作,低效的程序员可能会把改成 b )而不要写成while ( done = false )if ( (a b) = true )2.简化布尔表达式拆分复杂的判断并引入新的布尔变量拆分复杂的
34、判断并引入新的布尔变量把复杂的表达式做成布尔函数把复杂的表达式做成布尔函数一般控制问题一般控制问题如果某项判断需要重复做,或者会搅乱对程序主要流程的理解,那么可以把该判断的代码提取成一个函数,然后判断该函数的返回值。即使某项判断只做一次,如果把它放到一个命名良好的函数里,还是可以改善可读性,并能让你清楚的了解代码在做什么。新函数名为程序引入了一个抽象,可以清晰地在代码中说明该逻辑判断的目的。这样做比用注释好,因为人们更关心程序代码,可能不会读注释。而且这种描述更不容易过时。用决策表(表驱动)代替复杂的条件用决策表(表驱动)代替复杂的条件有时候有一个很复杂的判断,其中涉及到多个变量。这时用一个决
35、策表代替if或者case语句来执行判断会比穿梭在复杂的逻辑链路中更为高效。决策表查询操作写起来很容易,不会用到复杂难懂的控制结构。降低了复杂度,也就降低了出错的可能性。如果你用的数据变了,那么只需要修改决策表即可,无须改动代码。举一个抽象的例子,假设所有对象属于A、B、C三个组,你需要对这些对象指定一个类别号,规则如下:一般控制问题一般控制问题一般控制问题一般控制问题可通过复杂的逻辑结构指定类别号:if (a & !c) | (a & b & c) category = 1;else if (b & !a) | (a & c & !b) category = 2
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 班组安全的领航人培训课件
- 安全阀校验制度培训
- 财产清偿协议书
- 《税务会计》高职全套教学课件
- 货物抵押工资协议书
- 质量赔偿谅解协议书
- 购房合同还购房协议
- 2025年人民政协基层协商案例集
- 10KV架空线路巡视管理制度培训课件
- 肿瘤饿死癌细胞误区纠正
- 对外投资合作国别(地区)指南-马来西亚(2025年版)
- 心血管植入型电子器械植入术护理专家共识总结2026
- 2025年大学生提干选拔考试历年真题试卷及答案
- 2025四川宜宾市科技人才集团有限公司第三批员工招聘10人笔试历年参考题库附带答案详解
- 2025年中国邮政经济金融笔试及答案
- 2025年湖南省政府采购评审专家考试真题库及答案
- 《公路建设法律法规》课件 模块四 公路建设施工法律法规
- 钢结构劳务分包施工方案
- 旅店义工协议书
- 2026年及未来5年市场数据中国饲料用蛋氨酸行业市场调查研究及投资战略咨询报告
- 支气管哮喘急性发作护理指南
评论
0/150
提交评论