天天看点

设计原则:单一职责原则

单一职责原则(SRP)

单一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。这个原则的英文描述是这样的:A class or module should have a single reponsibility。翻译成中文,那就是:一个类或者模块只负责完成一个职责(或者功能)。

单一职责原则的定义描述非常简单:一个类只负责完成一个职责或者功能。也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。

如何判断类的职责是否单一

其实,评价一个类的职责是否足够单一,我们并没有一个非常明确的、可以量化的标准,可以说,这是件非常主观、仁者见仁智者见智的事情。实际上,在真正的软件开发中,我们也没必要过于未雨绸缪,过度设计。所以,我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构。

在不同的应用场景、不同的业务层面、不同阶段的需求背景下,对同一个类的职责是否单一的判定,可能都是不一样的。在某种应用场景或者当下的需求背景下,一个类的设计可能已经满足单一职责原则了,但如果换个应用场景或着在未来的某个需求背景下,可能就不满足了,需要继续拆分成粒度更细的类。

比如,在一个社交产品中,我们用下面的 UserInfo 类来记录用户的信息。

public class UserInfo {
  private Long userId;
  private String username;
  private String email;
  private String avatarUrl;
  // 省
  private String provinceAddress;
  // 市
  private String cityAddress;
  // 区
  private String regionAddress;
  // 详细地址
  private String detailAddress;
}
           

对于 UserInfo 类的设计,如果它里面包含的信息只是单纯地用来展示,像我们的抖音号一样,只是显示信息,那么 Userlnfo 现在的设计是满足单一职责原则的。但是,如果这个社交产品后来又添加了电商的模块,用户的地址信息还会用在电商物流中,此时的 UserInfo 类的设计就不满足单一职责原则,那么我们最好将地址信息从 Userlnfo 中拆分出来,独立成专门的用户地址信息。

那么在实际开发中,我们该如何判断一个类是否职责单一呢?这里,我们可以从一些侧面的指标判断一个类是否足够单一,比如,出现下面这些情况就有可能说明类的设计不满足单一职责原则:

  • 类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分;
  • 类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分;
  • 私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性;
  • 比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰;
  • 类中大量的方法都是集中操作类中的某几个属性,那就可以考虑将这几个属性和对应的方法拆分出来。

类的职责是否设计得越单一越好

单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是,如果拆分得过细,实际上会适得其反,反而会降低内聚性,也会影响代码的可维护性。所以,在设计类的时候,满足当前需求即可,不应过度设计。

继续阅读