有这么一个需求,输入员工姓名和工作时长,固定时薪 50 元/小时,然后输出结果。
你写的代码是不是这样的:
#include<stdio.h>intmain(){char name[20];int hours;printf("请输入姓名:");scanf("%s", name);printf("请输入工时:");scanf("%d", &hours);int salary = hours * 50;printf("员工:%s\n", name);printf("工资:%d\n", salary);return 0;}
把所有功能都放在了主函数中,主打一个能跑就行。
这种代码就是典型的烂到家了,如果改改需求,把数据的输入改成从配置文件读取,或者再增加奖金机制,主函数都得改。
这种代码就是低内聚、高耦合,一个模块塞入很多功能,牵一发而动全身。
下面对代码做第一版修改。
既然刚才说所有代码都被塞进了主函数,那就把他们全部封装成函数:
#include<stdio.h>voidinputEmployee(char *name, int *hours){printf("请输入姓名:");scanf("%s", name);printf("请输入工时:");scanf("%d", hours);}intcalcSalary(int hours){return hours * 50;}voidprintSalary(char *name, int salary){printf("员工:%s\n", name);printf("工资:%d\n", salary);}intmain(){char name[20];int hours;inputEmployee(name, &hours);int salary = calcSalary(hours);printSalary(name, salary);return 0;}
获取员工信息、计算薪资、输出结果,每个函数只负责干一件事情,这就是所谓的高内聚。主函数中,也只需要调用对应的函数就行。
但是老板又给出了新的需求:普通员工50/小时,高级员工80/小时。
继续来修改代码,可能你会这样写:
#include<stdio.h>voidinputEmployee(char *name, int *hours){printf("请输入姓名:");scanf("%s", name);printf("请输入工时:");scanf("%d", hours);}intcalcSalary(int hours, int level){if(level == 1)return hours * 50;elsereturn hours * 80;}voidprintSalary(char *name, int salary){printf("员工:%s\n", name);printf("工资:%d\n", salary);}intmain(){char name[20];int hours;inputEmployee(name, &hours);int salary = calcSalary(hours, 1);printSalary(name, salary);return 0;}
增加一个判断,不同的员工走不同的分支。
确实能解决问题,但新的问题又来了:等级规则、薪资规则、员工信息又开始纠缠在一起。
第四个版本,开始引入结构体,把员工的信息封装起来,同时计算薪资的函数改成这样。
#include<stdio.h>typedef struct{char name[20];int hours;int level;} Employee;voidinputEmployee(Employee *emp){printf("请输入姓名:");scanf("%s", emp->name);printf("请输入工时:");scanf("%d", &emp->hours);}intcalcSalary(Employee *emp){if(emp->level == 1)return emp->hours * 50;elsereturn emp->hours * 80;}voidprintSalary(Employee *emp, int salary){printf("员工:%s\n", emp->name);printf("工资:%d\n", salary);}intmain(){Employee emp;inputEmployee(&emp);int salary = calcSalary(&emp);printSalary(&emp, salary);return 0;}
主函数,也都围绕结构体展开。
这个设计确实不错,员工的数据放在结构体里面统一管理。
但是不省心的老板又提出了新的需求,岗位不仅有普通员工、高级员工,还有经理和总监,是不是还得在函数里面加代码。
不好意思,这就是开闭原则,当需求变化时,你应该通过添加新代码来扩展系统的行为,而不是修改已有的、已经测试过的代码。本来好好的代码,被你一改,又要重新测试。
最后,不得不拿出杀手锏,在结构体里面定义函数指针:
#include<stdio.h>typedef struct{char name[20];int hours;int (*calc)(int h);} Employee;voidinputEmployee(Employee *emp){printf("请输入姓名:");scanf("%s", emp->name);printf("请输入工时:");scanf("%d", &emp->hours);}intnormalSalary(int hours){return hours * 50;}intseniorSalary(int hours){return hours * 80;}intmanagerSalary(int hours){return hours * 120;}intcalcSalary(Employee *emp){return emp->calc(emp->hours);}voidprintSalary(Employee emp, int salary){printf("员工:%s\n", emp.name);printf("工资:%d\n", salary);}intmain(){Employee emp;inputEmployee(&emp);emp.calc = managerSalary;int salary = calcSalary(&emp);printSalary(emp, salary);return 0;}
所以,什么高内聚、低耦合?高内聚,就是模块内的东西都紧密相关,共同完成一个明确的目标。在函数层面,就是“一个函数只做一件事”。低耦合就是模块之间相互影响甚少,只通过稳定的接口交流,换一个模块就像换一块积木,不影响其他积木。


夜雨聆风