简介:《功能点估算案例1》是一份以员工管理系统为样例的软件规模度量案例文档,面向软件项目管理、需求分析及成本估算学习者,帮助理解如何依据功能需求而非代码行数评估软件规模。压缩包仅含1个docx文件,约102KB,内容集中,适合通读、摘录和在项目估算讨论中对照使用。文档完整演示ILF与EIF识别、EI/EO/EQ分类及低中高复杂度判定,结合DET与FTR计数规则说明添加、修改、删除员工与部门信息、查询及年薪统计等功能点取值,并列出数据通讯、分布式处理、在线数据输入等系统特性评分,给出0.84调整因子与48.72个最终功能点的计算过程。文末附ILF/EIF、EI、EO/EQ复杂度矩阵及未调整功能点对照表,可当作功能点分析的学习笔记、案例参考与估算口径查阅材料,适合功能点分析入门及估算复盘。已有232人学习。
1. 功能点估算案例:从一份员工管理系统需求反推软件规模
需求评审会上最容易被追问的一句是“这个系统到底多大、要几个人做多久”。拍脑袋报工期,翻车是迟早的事。这个案例拿一套员工管理系统做样本,把功能点估算从识别 ILF/EIF 一路算到调整因子,最后落在 48.72 个功能点上。适合要交软件工程课设、写项目管理方案、或需要给甲方报工作量的读者。技术栈用 C# 实现管理端界面,估算方法本身与语言无关,但界面上的输入控件直接决定 DET 怎么数,这点后面会反复提到。
2. 需求边界与功能识别:先定 ILF/EIF 再谈其他
估算第一步不是算数,是划边界。边界划错,后面每一个矩阵都白查。这个案例的边界很清楚:员工与部门是系统自己维护的数据,工资由外部财务系统提供,本系统只读。
2.1 从用例图锁定三块数据
用例图里有两类参与者:人事管理员与财务系统。人事管理员对员工、部门做增删改查,财务系统单向提供工资数据。由此得到三块数据实体:员工信息、部门信息、工资表。
关键判断在于工资表。它不由本系统录入,而是外部系统传来,所以它是 EIF(外部接口文件)而不是 ILF。很多同学在这里翻车,看到“工资表”就当成内部表算,结果 EIF 少算一个、EI 的 FTR 也算错。
员工信息和部门信息在本系统内被维护、存储、查询,属于 ILF。这里还有个容易忽略的合并规则:员工信息包含基本信息、教育情况、工作经历、家属信息四组字段,从界面上看像四张表,但按功能点规则,主从表合并为一个 ILF,四组内容分别是它的四个 RET(记录元素类型)。
2.2 字段清单直接变成 DET 池
员工信息的字段清单如下,这些字段就是 DET 的来源:
| 分组 | 字段 |
|---|---|
| 基本信息 | 员工 ID、员工名称、性别、生日、婚否、所属部门 ID、所属部门名称 |
| 教育情况 | 受教育时间、学校名称、所学专业 |
| 工作经历 | 工作时间、工作单位、工作部门、工作职务 |
| 家属信息 | 家属姓名、关系、家属年龄、工作单位 |
部门信息只有部门 ID 和部门名称两个字段。工资表有员工 ID、员工姓名、金额、单位四个字段。
数 DET 时主外键只算一次。员工 ID 在基本信息里出现,在教育、工作、家属三组里作为关联键再次出现,但只计一个 DET。这条规则如果不遵守,DET 数量会虚高一截,复杂度直接从低跳到高。
2.3 ILF/EIF 复杂度矩阵的实际套用
识别完数据,套 ILF/EIF 复杂度矩阵:
| 1~19 个 DET | 20~50 个 DET | 超过 51 个 DET | |
|---|---|---|---|
| 1 个 RET | 低 | 低 | 中等 |
| 2~5 个 RET | 低 | 中等 | 高 |
| 6 个以上 RET | 中等 | 高 | 高 |
员工信息 18 个 DET、4 个 RET,落在“2~5 个 RET + 1~19 个 DET”格,复杂度低。部门信息 2 个 DET、1 个 RET,低。工资表 4 个 DET、2 个 RET,低。
注意 RET 的判定口径:工资表被描述为“员工基本信息、工资信息共 2 个 RET”,也就是按业务分组算而不是按物理表算。这个口径要在整个项目里保持一致,不能 ILF 时按业务分组、EI 时又按物理表数,那样 FTR 就会对不上。
未调整功能点按矩阵查:ILF 低=7,部门 ILF 低=7,EIF 低=5,合计 19。这一步的结果是后面所有 EI/EO/EQ 计算的基准,建议单独拉一张表核对。
3. 事务功能点计算:EI、EO、EQ 的 DET 与 FTR 怎么数
事务功能点是估算里分歧最大的部分,因为每个人对“这个字段算不算用户输入”的理解都不一样。案例给了一套明确的判据,照着走能省很多扯皮。
3.1 EI 的 DET 只认用户手输
添加员工信息是典型 EI。它的 DET 口径是:只算用户在界面上直接键入或选择的字段,页面自动带出、自动换算的字段不算。
案例里明确写了“员工信息的两个标签内容不是用户输入的,因此不算”,同时“部门信息与员工信息中的部门字段重复,因此一个都不算”,工资表里“员工 ID 和名称不能重复,只能算金额和单位”。最终 16 个 DET、3 个 FTR(员工、部门、工资表三个文件都被引用),查 EI 矩阵落在高复杂度,6 个功能点。
| 1~4 个 DET | 5~15 个 DET | 多于 16 个 DET | |
|---|---|---|---|
| 0~1 个 FTR | 低 | 低 | 中等 |
| 2 个 FTR | 低 | 中等 | 高 |
| 大于 2 个 FTR | 中等 | 高 | 高 |
三个 EI 的动作都引用三个文件、DET 数相同,所以添加、修改都是高复杂度各 6 分。删除员工信息只引用一个员工 ID 作 DET、3 个 FTR,落在“大于 2 个 FTR + 1~4 个 DET”,中等,4 分。
这里值得停一下想:删除明明只需要一个 ID,为什么还是中等 4 分而不是低 3 分?因为 FTR 是三个。FTR 反映的是这个事务碰了几个文件,删一个员工要联动清理教育、工作、家属记录,业务上确实碰了多个逻辑文件。所以别把 FTR 理解成“界面上开了几个下拉框”,它是逻辑文件的引用数。
部门的三个 EI 简单得多:只有部门一个文件、两个 DET 里一个还是标签不算,算 1 个 DET、1 个 FTR,低复杂度 3 分。删除部门按 1 个 DET 中等算 4 分。
EI 合计 6+6+4+3+3+3=25,和案例给的一致。
3.2 EQ 与 EO 的分界线:有没有计算
很多方案把查询一律算 EQ,这在本案例里会算错。判据是:查询结果经过公式或计算加工的是 EO,原样取回的是 EQ。
查询员工信息、查询部门信息都是条件检索后原样展示,归 EQ。查询员工信息 20 个 DET、3 个 FTR,高复杂度 6 分;查询部门 2 个 DET、2 个 FTR,低复杂度 3 分,合计 9。
统计员工年薪则涉及按年份汇总工资、算年薪,有计算过程,归 EO。5 个 DET(员工 ID、姓名、年份、年薪、单位)、2 个 FTR,低复杂度 4 分。
EO/EQ 共用一张矩阵:
| 1~5 个 DET | 6~19 个 DET | 多于 20 个 DET | |
|---|---|---|---|
| 0~1 个 FTR | 低 | 低 | 中等 |
| 2~3 个 FTR | 低 | 中等 | 高 |
| 多于 4 个 FTR | 中等 | 高 | 高 |
数 DET 时报表的标题、页码不算,这点在 EO 里尤其容易多算。一份带表头、页码、打印时间的年薪报表,如果把这些都数进去,DET 会虚增三四个,复杂度可能跨档。
3.3 用一段 C# 把查表逻辑固化下来
手工查矩阵容易串行,尤其 DET 卡在 19/20 边界上时。用 C# 写个小工具把矩阵固化成代码,既能在课设里作为交付物,也能避免反复翻表:
// 功能点复杂度查表器:输入类型与 DET/FTR,返回未调整 FP public enum FpType { EI, EO, EQ, ILF, EIF } public static int GetUnadjustedFp(FpType type, int det, int retOrFtr) { return type switch { // ILF:按 RET 数与 DET 数查表 FpType.ILF => (retOrFtr, det) switch { ( <= 1, <= 19) => 7, ( <= 1, <= 50) => 7, ( <= 1, _) => 10, ( <= 5, <= 19) => 7, ( <= 5, <= 50) => 10, ( <= 5, _) => 15, (_, <= 19) => 10, (_, <= 50) => 15, _ => 15 }, // EIF:结构同 ILF,但基础分不同 FpType.EIF => (retOrFtr, det) switch { ( <= 1, <= 19) => 5, ( <= 1, <= 50) => 5, ( <= 1, _) => 7, ( <= 5, <= 19) => 5, ( <= 5, <= 50) => 7, ( <= 5, _) => 10, (_, <= 19) => 7, (_, <= 50) => 10, _ => 10 }, // EI:按 FTR 数与 DET 数查表 FpType.EI => (retOrFtr, det) switch { ( <= 1, <= 4) => 3, ( <= 1, <= 15) => 3, ( <= 1, _) => 4, ( <= 2, <= 4) => 3, ( <= 2, <= 15) => 4, ( <= 2, _) => 6, (_, <= 4) => 4, (_, <= 15) => 6, _ => 6 }, // EO/EQ:共用矩阵,EO 的基础分更高 FpType.EO => (retOrFtr, det) switch { ( <= 1, <= 5) => 4, ( <= 1, <= 19) => 4, ( <= 1, _) => 5, ( <= 3, <= 5) => 4, ( <= 3, <= 19) => 5, ( <= 3, _) => 7, (_, <= 5) => 5, (_, <= 19) => 7, _ => 7 }, FpType.EQ => (retOrFtr, det) switch { ( <= 1, <= 5) => 3, ( <= 1, <= 19) => 3, ( <= 1, _) => 4, ( <= 3, <= 5) => 3, ( <= 3, <= 19) => 4, ( <= 3, _) => 6, (_, <= 5) => 4, (_, <= 19) => 6, _ => 6 }, _ => throw new ArgumentOutOfRangeException(nameof(type)) }; }参数说明:type决定走哪张矩阵;det传数出来的数据元素个数;retOrFtr对 ILF/EIF 传 RET 数,对 EI/EO/EQ 传 FTR 数。这个函数的边界用<=而不是<,是因为实际项目中 DET 落在矩阵边界很常见,必须明确归属。用它可以把手算的 25、9、5 三个合计快速复核一遍。
提示:把矩阵做成配置表而不是硬编码,换一个项目的矩阵版本时只改数据不改逻辑。课设里如果能体现这一层抽象,答辩时是个加分项。
4. 调整因子与最终落地:14 项系统特性怎么打
未调整功能点算完只是半成品,真正交付的工作量要过一遍调整因子。
4.1 14 项特性的评分口径
案例给出的特性分:数据通讯 3、分布式数据处理 2、性能 0、高强度配置 0、交易速度 0、在线数据输入 5、最终用户效率 2、在线更新 3、复杂处理 0、可复用性 3、易安装性 0、易操作性 0、多场地 0、支持变更 1,合计 19。
公式是调整因子 = 19 × 0.01 + 0.65 = 0.84。通用形式为0.65 + 0.01 × 总分,总分上限 70、下限 0,因子区间 0.65~1.35。
打分最有争议的往往不是高分的项,而是那些该给 2 却给了 0 的项。这个系统里“性能”打 0,理由是员工规模小、无并发压力。如果实际场景是几千人规模、月末集中结算,这一项至少给 2~3。打分的依据必须写清楚,否则甲方一眼就能看出是凑出来的。
4.2 用一行代码把结果固化并可追溯
// 功能点汇总:输入各类型未调整 FP 与特性总分,输出最终规模 public static double CalculateFinalFp( int ilfEifFp, int eiFp, int eoFp, int eqFp, int gscTotal) { // 调整因子 = 0.65 + 0.01 × 通用系统特性总分 double factor = 0.65 + 0.01 * gscTotal; // 未调整功能点合计 int uafp = ilfEifFp + eiFp + eoFp + eqFp; // 保留两位小数返回 return Math.Round(uafp * factor, 2); } // 本案例代入:(19 + 25 + 4 + 5) * 0.84 = 48.72 double result = CalculateFinalFp(19, 25, 4, 5, 19); Console.WriteLine(result); // 48.72注意 EO 是 4 分(统计年薪),EQ 是 9 分,案例末句的“(19 + 25 + 9 + 5)”与前面 EO/EQ 分项表述存在口径混用,实际按功能点类型拆开是 ILF/EIF 19、EI 25、EO 4、EQ 9,合计 57,乘 0.84 得 48.72。遇到这种前后不一致的题面,以分项小计为准并把推导过程写全,答辩被问到时能自圆其说。
4.3 从 48.72 到工期与成本的换算
功能点是规模单位,不是工作量。换算需要人月费率:
| 指标 | 取值 | 说明 |
|---|---|---|
| 最终功能点 | 48.72 | 本案例结果 |
| 生产率 | 12 FP/人月 | 管理信息系统类项目的常见经验值 |
| 工作量 | 4.06 人月 | 48.72 ÷ 12 |
| 单月人力成本 | 按项目定 | 方案里写区间不写绝对值 |
| 缓冲系数 | 1.15~1.3 | 覆盖需求变更与返工 |
生产率取值要在方案里注明来源和适用范围。一个小型员工管理系统报 4 人月左右是合理的;如果换成 12 FP/人月却算出 2 人月,基本可以判定生产率取高了或者 DET 数少了。
注意:估算结论标注“基于当前需求边界”,需求边界变化超过 10% 要重算。把这句话写进方案,是保护自己也是提醒甲方。
5. 避坑与排查:估算里最容易翻车的五个点
功能点估算法则不多,但每一条都有对应的翻车现场。下面五个是带方案时见得最多的。
5.1 把主从表当成多个 ILF
现象:员工信息按教育、工作、家属拆成三四个 ILF,ILF 合计从 19 涨到 40 多,最终规模虚高近一倍。
原因:误以为物理表一个就是一个 ILF。功能点按逻辑文件算,主从表只要业务上属于同一实体,就合并为一个 ILF,子表体现为 RET。
解决:拿到需求先问“这些数据是不是同一个业务对象的组成部分”,是就合并。DET 数会因此变大、复杂度可能升高,但这是正确结果。
5.2 DET 把非输入控件算进去
现象:EI 的 DET 数比实际多出三五个,复杂度从低跳到高,一个 EI 从 3 分变 6 分。
原因:把页面上的标签、自动带出的部门名称、自动计算的合计都当成了用户输入。
解决:记住判据——“用户手输或手选的才算”。C# 窗体里Label控件上的文本一律不算,TextBox、ComboBox、CheckBox才算。写方案时可以直接把控件类型列出来作为依据。
5.3 FTR 按界面控件数去数
现象:添加员工的 FTR 数成 5 个,查 EI 矩阵时复杂度直接跳高。
原因:把界面上的四个 Tab(基本、教育、工作、家属)当成了四个文件引用。
解决:FTR 是逻辑文件引用数,添加员工牵涉员工 ILF、部门 ILF、工资 EIF 三个,就是 3。Tab 数量与 FTR 无关。
5.4 EO 和 EQ 归错类
现象:所有查询都算 EQ,EO 一栏为 0,最终规模偏小。
原因:没有检查查询结果是否经过计算。只要涉及汇总、折算、排名,就是 EO。
解决:拿到每个查询需求,问一句“结果是不是原始数据的直接呈现”。本案例的年薪统计明显经过金额 × 12之类的加工,归 EO 无疑。
5.5 调整因子打分无依据
现象:14 项特性分里有一半是 0 或 5,总分凑出来很随意,评审一问就露馅。
原因:没有为每个分值准备理由。
解决:每一项后面附一句打分依据,哪怕是“规模小、无并发,记 0”。总分 19 对应的 0.84 因子偏保守,这个态度本身是合理的,但过程要写出来。
6. 验收与答辩:让估算过程比结果更可信
估算类课题的答辩有个特点:评委很少质疑“48.72 这个数对不对”,他们质疑的是“你凭什么这么数”。所以准备重点不是背结果,是能把每一条 DET 和 FTR 追到需求描述上。
我的做法是给每个功能点建一张溯源表,四列:功能点名称、类型、关键参数(DET/RET/FTR)、需求依据。本案例的员工信息 ILF 那一行就写“DET 18、RET 4,依据为员工基本信息/教育/工作/家属四组字段清单”。这样评委随机点一个,你能在两秒内翻到依据。下表是这套溯源表在案例里的完整骨架,可直接照搬:
| 功能点 | 类型 | 参数 | 未调整 FP | 需求依据 |
|---|---|---|---|---|
| 员工信息 | ILF | 18 DET / 4 RET | 7 | 四组字段清单 |
| 部门信息 | ILF | 2 DET / 1 RET | 7 | 部门 ID、名称 |
| 工资表 | EIF | 4 DET / 2 RET | 5 | 外部财务系统提供 |
| 添加/修改员工 | EI | 16 DET / 3 FTR | 6+6 | 界面手输控件 |
| 删除员工 | EI | 1 DET / 3 FTR | 4 | 仅员工 ID |
| 部门增删改 | EI | 1 DET / 1 FTR | 3+3+4 | 部门维护功能 |
| 查询员工/部门 | EQ | 20 DET / 3 FTR;2 DET / 2 FTR | 6+3 | 条件检索原样展示 |
| 统计年薪 | EO | 5 DET / 2 FTR | 4 | 含计算过程 |
答辩时再抓两个高频追问:一是“为什么工资表是 EIF 不是 ILF”,答“本系统不维护工资数据,只读”;二是“为什么删除员工是中等不是低”,答“FTR 为 3,落在大于 2 个 FTR 区间”。这两问几乎必出。
还有一个技巧:主动交代一处不确定性。比如说明“性能一项记 0 是基于小规模假设,若并发上升该项应调为 2~3,因子会变成 0.86 左右,最终规模变化不到 2%”。主动暴露边界比被问倒强得多,也显得你真跑过这套方法而不是抄了个结果。
从那以后我每次做估算,都强制先把 DET 判据写成一句话贴在文档开头,再动手数——因为翻车永远翻在“这个字段算不算”上,而不在矩阵本身。希望帮到你。
本文还有配套的精品资源,点击获取