单一职责原则

释义

初看这个原则时,很容易将其错误地理解为:函数或类只应该保持有一种能力。这固然也是一个重要的设计原则,但是并不是单一职责原则所想表达的意思。这个原则最初的描述是:任何一个软件模块都应该有且仅有一个被修改的原因。
这里可能有点难以理解,对于软件而言,修改是永无止境的。如果只有一个原因能修改它,那设计之初就应该避免这个原因,做到十全十美。所以,这里的原因并不是指某个更改的意图。而是指,该模块所服务的对象,才是修改模块的原因。更进一步的解释就是:任何一个软件模块都应该只对某一类行为者负责。这也是原则名称里的职责含义。这里的行为者就是该模块所服务的对象,可以是用户或者另一个模块。

如果一个模块或者类需要服务于多个行为者,那么随着软件的迭代,行为者需求的更改,模块的代码会无限的膨胀下去,代码耦合度会大大提高。对此我们很难保证针对于某一行为者的修改,能够不影响到其他行为者的行为。最终的结局就是,更改一个问题,引出无数个其他的问题,项目失去维护的可能。

错误例子

下面通过一个不遵守单一职责原则的例子来认识这个原则。该模块用于控制一艘宇宙飞船。

class SpaceStation {
public:
    // 构造函数
    SpaceStation();
    
    void runSensor();
    void useSupplies(string type, size_t quantity);
    void reportSupplies();
    void loadFuel(size_t quantity);
    void reportFuel();
    void activeThrusters();
};

在学习数据库范式的时候,会遇到一张违反范式的超级表。这种表将所有的字段都放在了一起,企图在一张表里放下所有的数据,然而这会给数据库带来冗余过大,插入异常,修改异常等问题。同样的,这个类也是一个超级类,设计者试图用一个类来解决关于SpaceStation的所有需求。
按照单一职责原则,我们至少可以从这个类目前的实现中提取出四个行为者:sensors; supplies; fuel; thrusters。不同的行为者会有自己不同的需求,科学家会希望管理sensors,地面指挥中心希望管理好supplies,工程师会希望管理fuel,宇航员希望能够管理好thrusters
从这就大概就可以看出问题了,每个行为者都希望去更改SpaceStation,如果不加以阻止,SpaceStation会越来越膨胀,越来越复杂,这就是修改的“原因”太多了,违反单一职责原则所带来的后果。诚然,在初始阶段,这样去实现可以很快的完成任务,但是后续的维护会加倍地吞噬一开始所节省的时间。
解决办法就是把类负责不同行为者的部分单独拎出来,各自服务好各自的行为者,才可以从根源上避免上面所提到的问题。

简要修改

class Sensor {
public:
    void runSensor();
}

class Supplies {
public:
    void useSupplies(string type, size_t quantity);
    void reportSupplies();
}

class Fuel {
public:
    void loadFuel(size_t quantity);
    void reportFuel();
}

class Thrusters {
public:
    void activeThrusters();
}

class SpaceStation {
public:
    SpaceStation();
    
private:
    Sensor sensor_control_;
    Supplies supplies_control_;
    Fuel fuel_control_;
    Thrusters thruster_control_;
};

既然已经归纳出了四个行为者,所以把SpaceStation中相对应的功能拎出来,放到应该放的地方,也就是代码中所展示的四个类。这样不仅仅是对功能进行了封装,更重要的是保持了类的单一职责,每个类都有自己所属的服务对象。服务对象的需求可以无止境的变更,但不管怎样,这些变更都会被控制在一个类内,不会对外界产生任何影响。对于SpaceStation而言,它也不需要关心Sensor或是Fuel内部是如何实现的,它需要关注的就是这些类所提供的能力,按需索取即可。
当然,这唯一的弊端可能就是看起来复杂了一些,需要耗费的管理精力多了一些,但是随着项目的不断发展,这些弊端都会被抹平。
这样的修改并不是最终的版本,比如我们可以抽象出一个Report接口,SuppliesFuel都继承这接口来实现对应的报告需求。但对于完成单一职责原则的讲解,目前的修改已经足够了,我们成功地将职责进行了划分,下放到了对应的类中。

总结

单一职责原则是SOLID原则的第一个大原则,时刻遵循这一原则可以给各个模块划分出清晰的边界,这样可以防止修改代码所带来的风险扩散问题。并且能够实现信息的隐藏,将不必要的数据隐藏在内部,方便后续做替换和权限保护。更重要的是,单一的职责能够保证模块不会被各种各样的修改所破坏,有助于维护代码可维护性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值