单一职责原则

单一职责原则-SRP

一、怎样理解单一职责原则

1.概念

​ SRP :Single responsibility principle 的缩写。一个函数,或者一个模块,应该只负责一类行为。

​ 需要注意的是,每个函数应该只做一件事,并且把这件事做好。这是SRP 在实现底层细节的时候的一个体现。SRP是一种思想,需要用心体验。大到整个软件,小到一个函数,变量。都适用。不要把它当成一种技巧。

2.演化

​ 单一职责最开始的描述:

任何的一个软件,有且仅有一个被修改的理由。

​ 那为什么要修改呢?因为客户需求变化,因为使用者需求变化。所以可以换一个描述:

任何的软件(模块,函数),仅仅对一个使用者需求负责。

​ 虽然是不同客户的需求,但是也可能几个客户对软件的需求修改是一样的。所以再换一个:

一个模块或者函数,只对某一类行为负责。

用自己的话讲,就是看谁用这个模块(函数)。使用者不同,软件职责就可能不同,职责不同就要想办法分开,强行放在一起,就违反SRP

二、案例

1.案例问题说明

​ 假设有一段需要计算员工工作时长的函数。

float employee_calculate_work_hours(employee_desc *employee_info)
{
    //996 工作制
    float x = 12*6;
    return x;
}

​ 它可能有两个用处:

  • 计算工资。
  • 用来做考核。
float employee_calculate_salary(employee_desc *employee_info)
{
    //计算工资一小时一百块
    float salary = 100*employee_calculate_work_hours(employee_info);
    return salary;
}

float employee_check(employee_desc *employee_info)
{
    //考核分数计算,工作时长*基本能力系数
    float check = 2*employee_calculate_work_hours(employee_info);
    return check;
}

现在需求变动,加班没有工资。每天按照8小时来计算:

float employee_calculate_work_hours(employee_desc *employee_info)
{
    //虽然996小时工作,但是工资只有5*8.
    float x = 8*5;
    return x;
}

​ 然后测试了一下,工资计算非常合理,就提交了代码。

​ 最后就出现,员工的考核结果也受到了影响。因为考核也按照8*5来计算了。

虽然都是计算工作时长,但是因为给不通过的部门使用,employee_calculate_work_hours 函数的职责在这种情况下其实不一样。

​ SRP原则要求,这样的代码必须分开。

2.案例问题解决

​ 修改后的代码:

typedef enum work_hours_responsibility
{
    E_RESPONSIBILITY_CALCULATE_SALARY,
    E_RESPONSIBILITY_WORKER_CHECK
}work_hours_responsibility;


typedef struct employee_desc
{
    //可能还有其它的属性,先略过了。
    work_hours_responsibility responsibility;
};


typdef int (*calculate_work_hours_callback)(void *param);
typedef struct calculate_work_hours_map_desc
{
    work_hours_responsibility responsibility;
    calculate_work_hours_callback fn;
}calculate_work_hours_map_desc;

static float employee_cal_work_hours_for_salary(employee_desc *employee_info);
static float employee_cal_work_hours_for_check(employee_desc *employee_info);

//采用数据驱动编程,提升代码可读性。
//因为职责不同,所以分成两个函数
calculate_work_hours_map_desc calculate_work_hours_map[]=
{
    {E_RESPONSIBILITY_CALCULATE_SALARY,employee_cal_work_hours_for_salary},
    {E_RESPONSIBILITY_WORKER_CHECK,employee_cal_work_hours_for_check}
};

//在不同的场景中,设置不同的责任,则计算的方法也不同。
float employee_cal_work_hours_for_salary(employee_desc *employee_info)
{
    float x = 5*8;
    return x;
}

float employee_cal_work_hours_for_check(employee_desc *employee_info)
{
    float  x = 12*6;
    return x;
}

//修改后函数接口不变,但是不同场景计算结果已经不同。
float employee_calculate_work_hours(employee_desc *employee_info)
{
    int i = 0;
    for(i = 0; i < sizeof(calculate_work_hours_map)/sizeof(calculate_work_hours_map[0]); i++)
    {
        if(calculate_work_hours_map[i].fn != NULL)
        {
            return calculate_work_hours_map[i].fn(employee_info);
        }
    }
    return 0.0;
}

三、无脑遵守SRP会有什么问题?

问题:

如果一开始就考虑SRP,可能会纠结,函数就不能复用了吗?

就像上述的例子。难道一开始,需求变化之前,计算时间的函数,就要分开写?刚开始写两个一模一样的函数,就是命名不一样?

那什么时候就要分开,什么时候就要复用?

解答:

我理解的是,如果当前你和你的团队想不到当前函数可能被使用的多个场景。就不要着急使用单一职责原则。先让其复用。

如果出现了需要分别处理的情况,则需要对代码进行重构,重构的时候,再使用 SPR原则。

SPR原则,应该是防止你已经知道或者能预料到这个软件会有多个职责,但是依然要把它们挤在一起的这种情况。

四、参考文献

《架构整洁之道》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值