为什么要估规模
规模可以帮我们:
- 依据历史数据策划,例如估算工作量、工期。
- 归一(Normalize)不同项目作比较。
- 知道现在水平。
依据历史数据策划先把项目分成组件,参考以往类似的组件所花工作量,估算整个项目的总工作量。规模大小可简单看成是组件的数量。如果是新开发,以前从未做过同类开发,就只能靠个人经验直接估算工作量或工期,但是如果以前做过类似的工作,就可以参考以前的历史数据估算。
规模可以帮我们把不同项目归一。例如验收测试缺陷数无法比较,但缺陷率(缺陷数/规模)便可以比较;生产率(规模/所花总工时)便可比。
有了归一后可比较的系数,个人/团队便更清楚当前的水平(质量或生产率)是否在上升或下降。
为什么不应用代码行数(LOC)
在1996年前,IBM一直使用代码行数估算规模。之前一直都使用近似机器语言的Assembly Lang,但为了提升效率,引入了高层语言PL/S,发现不能再用代码行数来估算规模,因PL/S只需要更少代码行数也能完成同样功能:
Assembly | PL/S | |
---|---|---|
代码行数 | 17,500 | 5,000 |
工作量(月) | 30 | 12.5 |
工作量(工时) | 3,960 | 1,650 |
每月代码行数 | 583.33 | 400.00 |
等同的Assembly代码行数 | 17,500 | 17,500 |
等同的Assembly人月 | 583.33 | 1,400.00 |
功能点 | 100.00 | 100.00 |
功能点/人月 | 3.33 | 8.00 |
上表是IBM(1968 ~ 1975)对两种编译器的统计:
每个月PL/S产出代码行数(400)反比Assembly(583)少,但是如果用功能点数便能真正反映生产率的提升:
PL/S:8.00对比Assembly:3.33
只适用于编码
代码行数只能反应编程的工作量,但编码仅占项目总工作量的一部分,如果把项目按工作量分成以下5个部分,代码行数只能用于第二部分编码(25%),其他4部分(30% + 20% + 15% +10%)都不合适。
活动 | 成本(%) | |
---|---|---|
1 | 发现与修正缺陷 | 30 |
2 | 编码 | 25 |
3 | 支持类文档 | 20 |
4 | 会议沟通 | 15 |
5 | 项目管理 | 10 |
总计 | 100 |
与功能点比较
能估算好项目规模可帮助我们更好估算工作量,规模应具备以下条件:
- 要和工作量密切相关。
- 要容易数得出来。
- 容易在项目早期可以估算到。
功能点比较符合以上条件,只要功能需求明确,就可以估算出对应的功能点数。
因功能点估算已经是国际标准,基于功能点的度量数据可以与其他国家的标杆对比。
怎样估算简化功能点(SiFP)
有了功能性需求,并识别出系统范围,就可以开始估算功能点。
- 数据功能(简称实体)的数量 × 7 (实体是系统要管理的数据)
- 业务功能(简称行为)的数量 × 4.6 (行为可以简单看成是增删改查等功能)
简化功能点数=上面1和2的总数
例如,附件例子一潜水学校新开发项目:
- 识别出3个实体和24个行为,得出简化功能点数131.4 = 3 × 7 + 24 × 4.6
潜水学校二次开发项目:有些功能删除,有些功能变更,就需要分开计算动态功能点和静态功能点:
- 静态功能点可以看成是整个系统的功能点数,所以变更的功能点数不会引起影响,但删除的话就需要减去。
- 动态功能点主要是用于估算本期开发项目的工作量,因为无论变更功能或者删除功能,都会导致有开发工作量,所以不能是零或负数。国际功能点的规定很简单,都加起来。
所以这次二次开发的动态功能点是:
2 × 7 + 11 × 4.6 (增加2个实体和11个行为)
+ 1 × 7 + 3 × 4.6 (变更1个实体和3个行为)
+ 0 × 7 + 2 × 4.6 (删除两个行为)
= 64.6 + 20.8 + 9.2
再加上 4.6 (因为有数据转换,所以需要加一个CFP,等于4.6)
最终动态功能点是99.2 (= 64.6 + 20.8 + 9.2 + 4.6)
静态功能点: 新开发加上新增减去删除的功能点
= 131.4 + 64.6 – 9.2
= 186.6
从故事点转简化功能点
因故事点只是每个团队自己定义,无法用来作为组织级标准衡量规模,所以很多公司(尤其是银行)转用功能点来衡量软件开发规模。它不仅可用于公司内,也适用于行业标杆,功能点也适用于公司内或公司间结算,例如软件维护期开发工作量变化很大,也难以事前预估,所以有些银行会按最终开发出来的功能点数结算使用部门付开发部的费用,减少争议。
虽然功能点分析源自70年代,但由于计算较复杂,一直未普及,有些人觉得国际功能点协会(IFPUG) 的功能点算法(FPA)太复杂。针对这问题,国际功能点协会简化本来的功能点算法,推出简化功能点(SiFP),减少了估算的工作量与学习难度(例如培训可以从以往的2天半降到半天)。因简化功能点不考虑实体/行为的复杂度,它与本来功能点的估算有± 15% ~ 20% 偏差(详见附件)。
因敏捷团队是按每轮迭代估算(而不是一次性估整个项目),偏差就可以接受(例如2周一个迭代,偏差大概1.5天),所以SiFP能适用于敏捷多次迭代估算。所以越来越多团队开始从故事点转简化功能点,但由于不熟识功能点估算是从用户角度估算,而非从开发工程师的视角,团队初次估算SiFP通常有误。下面是某成都团队从故事点转简化功能点的案例:
客户:我们以前一直使用故事点,为了更好做量化管理,我们新的项目开始使用简化功能点SiFP。
|
客户:是的,每个行为都要花一定开发工作量。
|
总结
虽然简化功能点(SiFP)比传统国际功能点(IFPUG)简单易学,但开发人员还是容易用工程师的视角来估算(本应用用户的视角),导致计算错误。所以要多看案例并做练习,才能把握(能参加培训会更好)。
附件
简化功能点(SiFP) 简介
它是做什么的?
应用软件开发的客户需求可分成3类:
- 功能性需求
- 技术需求
- 质量需求
第二类和第三类归为非功能性需求。功能点主要是针对功能性需求,目的是提供对客户有意义的功能点数,来客观地衡量软件规模。
该如何去做?
简化功能点(SiFP)主要分为两类度量:
- 数据功能——实体 (逻辑文件 Logical File)
- 事务功能——行为(基本过程 Elementary Process)
简化功能点估算步骤:
- 确定功能点分析类型
- 识别分析范围和应用边界
- 计算数据类型功能点
- 计算交易类型功能点
- 计算功能点
1、3种SiFP计算类型
- 开发(Development )
DSFP = ADD + CFP
- 应用 (Application or Baseline after the initial development)
ASFP = ADD
- 更新/增强功能与维护 (Enhancement)
ESFP = ADD + CHG + DEL + CFP
ADD 新增 CFP 数据转换,包括新建系统首次设定数据 CHG 变更 DEL 删除 |
2、识别分析范围和应用边界
用虚线标示系统边界:
3、计算逻辑文件数
- 关于计算规则,详见“逻辑文件”
4、计算基本过程数
- 关于计算规则,详见“基本过程”
5:计算功能点
- 每个逻辑文件 = 7.0 简化功能点
- 每个基本过程 = 4.6 简化功能点
逻辑文件 (Logical File)
下面在计算实例里简称“实体”以方便理解
- 用来储存内部或外部数据,是用户可识别的逻辑相关的数据组或控制信息组,在被度量应用边界内部维护。
(用户可识别 指数据或事务需求是被用户和软件开发人员双方共同认同并理解的。 例如:用户和软件开发人员双方都认同人力资源应用有维护和存储员工信息的功能。)
注意
逻辑文件包括两类不同的用户需求数据:
- 功能性数据
- 非功能性数据
功能性数据是用来满足用户功能需求的数据。例如销售、银行账号、供应商、人员等信息。
非功能性数据主要是为了满足易用性(支撑下拉菜单所需的数据,可输入数据的上下范围等),或性能方面(用于查询数据的索引index),或可维护性(配置参数)。
只有第一类功能性数据才算是逻辑文件。
功能点估算只包括功能性需求,国际功能点(IFPUG)有SNAP,专门针对非功能需求估算。
基本过程 (Elementary Process EP)
基本过程是对用户有意义的最小活动单元。例如:添加员工的用户需求包括建立工资和家属信息。只有添加所有员工信息,才能创建员工信息记录。单独添加一些信息使添加员工业务处于不持续状态,只有员工工资和家属信息都添加后,这个活动单元才能完成且业务处于稳定状态。
识别基本过程
为了识别基本过程,需要执行以下活动,把功能用户需求分解为最小活动单元,使其满足下面条件:
- 对用户有意义
例如功能用户需求要求在应用中添加新员工的能力。 - 构成一个完整的事务
例如:用户定义的员工信息包括工资和家属信息。如果家属人数大于零,添加员工信息时必须包括家属信息。本例中,添加员工信息(不包括添加地址、工资和家属信息)不满足本规则。 - 自包含
例如:除非输入所有的必需信息并且完成所有处理步骤,如验证、计算、更新ILFs,添加过程才是自包含的。 - 让应用程序的业务保持持续状态
例如添加员工的用户需求包括建立工资和家属信息。只有添加所有员工信息,才能创建员工信息记录。单独添加一些信息使添加员工业务处于不持续状态,只有员工工资和家属信息都添加后,这个活动单元才能完成且业务处于持续状态。
识别活动单元为基本过程需要满足以上所有规则。
识别基本过程主要目的
基本过程的主要目的可识别为下列情形的一种:
- 改变应用行为
- 维护一个或多个ILFs
- 呈现信息给用户
实例:识别基本过程 (EP)
下面是某预约网约车过程:
活动名称 | 描述 |
---|---|
预约申请 |
客户发出预约请求到预约系统,预约信息包括:日期、时间、上车的位置和目的地位置。 |
接收预约 |
预约系统收集客户预约请求,并把预约数据记录在数据库。 |
检查是否有可用的车/司机 |
预约系统从预约数据库中查看是否有合适的车/司机,如果能找到合适的时间、日期,能配合预约请求的话,看看司机是否有空档。如能找到,在系统更新内部状态为是,否则为否。 |
寻找其他可选 |
如果状态是没有,预约系统继续在数据库搜索有没有接近的预期时间,否则写没有合适请求的出发地点和目的地。 |
提供预约信息 |
预约系统自动发出通知到客户,是否有合适空档或者提供接近的日期时间。 |
处理预约 |
客户回复预约系统接受或拒绝,预约系统把反馈记录在预约数据库。如果反馈是接受,预约系统会继续统计客户详细预约信息。如果客户拒绝,预约系统就会回复收到,并终止过程。 |
分派司机 |
如果客户接受,预约系统会指派司机到已确认的日期、时间、上车地点和目的地。预约系统把这个记录在数据库中,并发信息通知相关司机。 |
接乘客 |
司机按约好的日期时间、上车的地点接乘客,然后发信息到预约系统,通知乘客已经上车。 |
完成预约请求 |
预约系统在数据库中记录已经完成整个过程。 |
分析能否满足所有 EP 识别规则,判断能否独立成为基本过程 (EP),部分例子
预约申请
- 是否对用户有意义,是客户功能需求的一部分。是。
- 是否构成一个完整的事务?否:预约申请本身不是一个完整的交易,因为过程必须也包括预约请求信息,收到其他可选的档期这些步骤,都不可以分离。
- 是否自包含,可以独立存在?否:例如接受预约申请,查看是否有档期,查看有没有其他接近的档期等,都是一些必需的相关步骤去完成这个基本过程。
- 是否让应用程序达到稳定状态?否:整个业务需求只能在收到预约信息,发送、接受、处理、反馈给客户才算是完成稳定状态。
分派司机
- 是否对用户有意义,是客户功能需求的一部分。是
- 是否构成一个完整的事务?是:分配到司机是一个完整的交易,包括收到司机的确认,把信息记录在系统中并通知司机。
- 是否自包含,可以独立存在?是:分配到司机本身可以独立存在。
- 是否让应用程序达到稳定状态?是:因为当司机被分配后,是完全满足业务的需要。
接乘客
- 是否是客户功能需求?是。
- 交易是否完整?否:接乘客本身不算一个完整的交易,因为预约系统必须也记录这个信息。
- 是否自包含,可以独立存在?否:确认预约申请是下面一个必须执行的过程,来完成这个基本过程。
- 是否让应用程序达到稳定状态?否:整个业务需求只能在预约系统确认沟通,已经接到乘客,然后系统也把记录更新到预约系统才算完成。
业务过程/活动 | 基本过程 (EP) 必须满足所有EP识别规则 |
---|---|
|
如果单独来看每个活动,不能满足基本过程的条件。它们必须要结合在一起,才能满足所有基本过程的条件。基本过程包括左面所有过程/活动。 |
|
业务流程、活动满足所有基本过程的要求,可以当成一个基本过程。 |
|
业务流程、活动满足所有基本过程的要求,可以当成一个基本过程。 |
|
如果单独来看每个活动,不能满足基本过程的条件。它们必须要结合在一起,才能满足所有基本过程的条件。基本过程包括左面所有过程/活动。 |
1.潜水学校:开发项目
描述
一所潜水学校需要一套用来管理合同员工(教练)、设施、轮班工作的系统。目的:有效地管理教练在潜水设施和几艘旅游潜水船上有关潜水课程/短途潜水的轮班工作。
功能需求
RF01
为处理教练保险单文件,并符合法例,学校需要为每个合同员工储存以下资料:
- 序列号(独特、作为索引、不能重复)
- 姓名
- 居住地址
- 城镇
- 邮政编码
- 电话号码
- 是否持有航海执照
为了跟踪每个合同员工的“职业生涯”,学校决定给予他们以下分类:
- 潜水长
- 助理教练
- 教练
这些分类是固定的,并不会随着时间而转变:每个合同员工会按顺序分配到合适的类别,代表个人“职业生涯”的发展(例如,一个新员工开始是潜水长,随着时间的推移,他会成为教练)。
使用表单输入、显示、编辑和删除合同员工的数据。会有一个独立列表框,显示序列号、名和姓 (但没有附件明细),来选择要编辑、删除或详细查看的是哪位员工的数据。
用功能键激活所有的功能,并最终产生一个结果或错误信息。“删除合同员工”只是在逻辑上删除,没有数据会被物理删除,但会被标记为作废。只要有与其相关的工作班次,合同员工就不能被删除。
RF02
学校还需要管理设施(潜水、船或橡皮艇),每一个设施都有独特的友好名称(例如:潜水莫格利亚、潜水帕拉、蓝箭艇、格里大艇、嘉莉花等)。
对于每种类型的设施,必须存储以下信息:
- 设施识别名(独特、作为索引、不能重复)
- 描述
- 类型
- 能容纳的人数
- 可用汽缸数
- 是否有厕所
- 是否有饮用水储备
必须创建表单来输入、显示、编辑和删除设施数据;如果被分派到轮班,就不能删除设施。使用独立的列表(不显示附件细节),以便选择要编辑、删除或详细查看的数据,列表只展示标识名称、描述、类型。
用功能键来激活某功能,并最终生成错误或结果信息。
RF03
最后,为了有效管理分派轮班(shift)的覆盖范围,学校需要处理合同员工以下轮班信息:
- 工作轮班识别名(独特、作为索引、不能重复)
- 可用的合同员工编号(使用下拉框挑选)
- 提供本轮班可用的日期
- 可用期的开始日期
- 可用期的结束日期
- 首选设施(使用下拉框挑选)
- 状态(最初预设置为“预计轮班”)
必须创建表单来输入、显示、编辑和删除轮班的有效信息。为了方便选择对哪些数据进行编辑、删除或查看详细信息,会独立显示没有附件细节的数据列表(如不显示可用性标识号、合同员工序列号)。用功能键将激活该功能,并最终生成错误/或结果信息。
RF04
每个合同员工可以提供不止一个可用轮班(availability),每一个轮班最初都设定为“预计轮班(tentative shift)”状态。当分配协调各合同员工的可用轮班作为一个“轮班”内的可用资源时,秘书处在一个“分配轮班(assigned shift)”内使用特定命令选择(转换)所需的可用轮班(availability),她可以更改潜水期的开始和结束日期,并可以将之设定为“分派轮班”状态。删除“分派轮班”与删除“预计轮班”的功能/步骤类似。
RF05
有以下查询:
- 找出合同员工中谁已经有许可证,因此能够以“船夫”的身份带队出海,显示属性:序列号、姓和名、总人数。
- 选择当前某月份(或其他月份和年份)收到的所有可用合同员工——显示的属性:序列号、姓名、类别、可用期的开始日期和结束日期以及对设施的偏好。
- 根据档案中设施的数量和类型(包括考虑潜水和船数量),计算学校管理的最大人数。
- 计算每个类别的员工人数(潜水主任;助理教练;教练):按类别列出总计和小计。
- 通过显示姓名和姓氏,显示最“忠诚”的员工,即年初以来提供最多可用时间段的前3名员工。
- 上面查询4的增强版:通过类别细化——换句话说,不仅仅是显示数量,可选择某个类别的相关合同员工列表(姓和名)与其总数量。
答案与解读
共3个实体:
- 合同员工
- 管理设施
- 轮班
你可能会问:那些合同员工的职称是否也应该是一个实体?(因需要花工夫开发)
这不应该是一个实体。因为人员的职称必须依赖人员的信息挂在一起,不可以独立存在,就好比我们要维护员工信息,假如也要维护员工的家属信息,这个家属信息就不能算另外一个实体,因为没有人员的话,家属是不能单独存在的。区分原则不是根据是否要产生开发的工作量,而是从用户角度看,这个实体能否独立存在和维护。否则功能点的估算就只是根据个人对开发工作量的估计,而不是从用户角度看功能的客观判断。
每个实体对用户来讲,都有新增、展示、修改、删除4个功能。在人员管理里,还有一个功能是显示一个可选的列表,方便用户选择,这功能是增查改删以外的第5个功能。
设施管理也同样有这个列可选设备设施的一个展示框这第5个功能。
在轮班管理里面,除了增、查、改、删和展示外,它里面有两个下拉框功能:
- 让客户挑选相关设施的Combo-box下拉框
- 让客户选人员的框
你可能会问,这2个下拉框功能是否不应该算额外的功能,而是属于“轮班”的增查改删基本功能的一部分?
我们可以这样想:从用户的角度来看,如果没有这两个下拉框的功能,基本的增查改删功能是否可以实现;现在做了两个下拉框的功能,是额外的新增功能,更方便用户去选择,所以这两个算是额外功能。
也可参考IFPUG关于EI/EO/EQ 的识别要求;基本操作(Elementary Process)必须符合以下三条之一:
- 使用独特处理逻辑,与应用中其他“行为”(EI/EO/EQ) 的处理逻辑不同
- 在该处理中识别出来的数据元素是与应用中其他“行为”(EI/EO/EQ)的数据元素不同
- 在该处理中引用的“实体”(ILF 和EIF)与其他“行为”(EI/EO/EQ)所引用的不同
它要列出所有符合条件的数据元素到这下拉框,类似一个新的报表,所以算一个行为。基于同类原因,挑选相关设施的Combo-box下拉框,选择可用合同员工Combo-box下拉框等各自也算一个行为。
在轮班里,还有一个展示可选的轮班功能,另外是分配轮班功能。还有最后的RF05六个查询功能。
得出共 24(=5+5+6+2+6)行为,加3实体,所以按简化功能点每个实体×7,每行为×4.6得出,共新增131.4(=3×7+24×4.6)简化功能点,详见下面列表:
计算功能规模
DSFP = ADD + CFP
因为没有数据转换,所以 CFP=0,DSFP = (110.4+21) +0 = 131.4 SiFP
因是首次开发, ASFP = ADD = 131.4 SiFP
2.潜水学校:FEM项目
描述
参照之前的潜水学校系统,对功能进行了增强,并提出了该软件的功能优化维护项目(FEM)。
功能需求
RF01
用户想要取消合同员工删除功能。
RF02
在短途潜水里, 在潜水设施管理中能管理船上医生的存在或缺失。The presence/absence of a ship doctor during excursions must be managed in the file DIVING FACILITIES.
RF03
出于税收和安全原因,不再需要删除可用轮班这项功能 For tax and safety reasons the function to delete availability shifts will no longer be required.
RF04
用户还需要管理课程参与者信息和他们参加的那个短途潜水信息:
- 管理参与者的信息包括:参与者ID、姓、名、出生日期、潜水执照、执照日期。
参加短途潜水:参与者ID、轮班编号、出游日、天数、最终考试是否通过。 - 用列表框 (包括参与者ID、姓、名)来选择轮班中的参与者。
- 使用原本应用程序中已经有的列表框选择轮班。
- 用功能键将激活这功能,并最终生成错误信息/结果。
用功能键初始填充课程参与者信息,参与者信息源自以前参与者信息的备份数据。
RF05
用户还需要能够在课程结束时颁发出席证书给在短途潜水中登记的所有参与者。除了管理参与者基础数据外,还需要管理参与者所登记的轮班、轮班日期、时长、教练的姓名和医生(如在场)的姓名。该功能使用原本应用程序中已经可用的功能:选择轮班。用功能键将激活这些功能,并最终生成错误/结果消息。
RF06
用户还需要能够向合同员工颁发“教员身份参与证书”,其中的信息除了基础数据外还包括:轮班ID、教练ID、出游日、船医(如果有的话)。对于轮班选择,将使用原本应用程序中已经有的列表框。用功能键将激活这功能,并最终生成错误信息/结果。
答案与解读
RF02 变动了潜水设施的内容,所以设施实体有变更。
因为设施的信息有变更,导致跟这实体相关的行为,包括新增、编辑和展示这3个行为都会有变更。
另外加了两个要管理的实体:
- 参与者
- 短途潜水
不需要合同员工的删除功能,所以是个行为删除。
在参与者的管理中,除了增加、改动、展示和删除4个功能以外,还有可以挑选参与者的下拉框功能。
两个证书的功能:
- 给教练的证书
- 给参与者发证书
对应每个短途潜水也需要有添加、改动、展示、删除的4个功能。那个删除轮班功能也被删掉了。 增加了两个实体——参与者与短途潜水旅行登记。
Q: 为什么短途潜水旅行登记算一个实体?
A: 因它包括的信息都不能归入已有的“参与者”、“合同员工”、“设施”、“轮班”实体里,例如那位参与者参加了哪个班、考试分数等。也可参考IFPUG关于ILF/EIF (实体)的识别要求,必须符合以下条件:
- 数据的集合必须是逻辑相关的并且是用户可以识别。
- 这些数据或者控制信息必须是在本应用的边界内被维护。
总结:
- 实体方面增加了2个实体,设施实体有变更。
- 行为方面主要的在短途潜水旅行方面增加了4个增删改查的功能和5个参与者的功能(因为在里面加了一个下拉框功能),增加了2个证书功能。改动了设施的增加、编辑和展示3个行为,删掉了2个行为。
所以动态功能点是增加的功能点64.6 (=2×7 +(4+2+5)×4.6),变更 20.8 (=7 + 3×4.6),删除9.2 (=2×4.6),总共的动态简化功能点94.6。
静态功能点依据上面练习一那的131.4,加上增加的功能点64.6,减掉删除功能点9.2,得出变更后静态功能点186.8。
计算功能规模
ESFP = ADD + CHG + DEL + CFP
因为有数据转换:初始填充课程参与者信息作为一个基本过程,所以CFP=4.6
ESFP = (64.6 + 20.8 + 9.2) + 4.6 = 94.6 + 4.6 = 99.2 SiFP (本FEM项目动态功能点)
软件开发后的静态功能点: ASFPA = ASFPB + ADD – DEL = 131.4 + 64.6 – 9.2 = 186.8 SiFP
与国际功能点(IFPUG)的偏差
例子:
- 新开发某会计付款系统。
- 实体: 包括管理发票、付款、供货商。
- 行为:包括对每个实体的展示、增加、修改和删除/取消。
- 使用IFPUG数EI、EO、EQ、ILF、EIF 每类的调整前功能点数,加起来得出调整前功能点数FP=82 (可参照下面表一得出82,例如 EI 24 = 4×3 + 2×6)。
- 使用SiFP估算实体和行为数,计算得出FP=104。
- IFPUG / SiFP 得出的实体数量,与行为数量都一样(实体(=ILF+EIF)=5;行为 (=EI+EO+EQ)=15)。
- 因为简化功能点只是不区分实体与行为的复杂度(高中低),取平均值。原理一样,虽然个别估算有差异,但平均下来与IFPUG的估算没有结构性偏差。
参考References
- Brigido, Sergio. “FPA Rules interpretations for Elementary Processes ” , IFPUG Whit paper. (2022)
- SiFPA Assoc. “Simple Function Point Functional Size Measurement Method, Measurement Examples ” , SiFP-0.1.00-EX-EN-01.01 (2014)
原文地址:https://blog.csdn.net/u011250455/article/details/135813868
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_64879.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!