设计原则01:单一职责原则(SRP)

我们都知道单一职责原则,其原本的英文描述是:A class or module should have a single responsibility。

这里描述的对象有两个,一个是class(类),一个是module(模块),模块是比类更加抽象的概念,但是原则上都是一样的。

单一职责的定义非常简单,一个类只负责一个职责或者功能。那么,如何判定这个类的职责是否足够“单一”呢?

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

假如说在一个社交产品中,我们用下面的UserInfo来记录用户信息,判断一下这个类的职责是否满足“单一职责”呢?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15

public class UserInfo {
  private long userId;
  private String username;
  private String email;
  private String telephone;
  private long createTime;
  private long lastLoginTime;
  private String avatarUrl;
  private String provinceOfAddress; // 省
  private String cityOfAddress; // 市
  private String regionOfAddress; // 区 
  private String detailedAddress; // 详细地址
  // ...省略其他属性和方法...
}

对于这个问题的答案是要结合具体场景来看的,并不是一个固定的答案,在不同的场景甚至不同阶段的需求中,都有可能发生变化,这里举几个例子。

  • 地址信息只用来展示,那么这个类的设计就是合理的;如果有电商模块,那么地址的信息就应该被独立拆分出来;
  • 如果公司发展出了独立的APP,需要做统一认证,那么跟身份相关的字段就应该抽取出来作为独立的类;

所以说,评价一个类的职责是否单一是需要结合具体业务的,通常情况下,我们可以先做一个粗粒度的划分,满足业务需求,随着业务的发展,可以逐步拆分成更细的类,这就是所谓的**“持续重构”**。

但是有几条判断原则是可以去参考的:

  • 类的代码行数、函数或属性过多;
  • 类依赖的其他类过多,不符合高内聚、低耦合的设计思想;
  • 私有方法过多;
  • 比较难给类起一个合适的名字,很难用一个业务名词概括,说明职责定义不够清晰;
  • 类中大量方法都在集中操作类中的几个属性,比如userInfo中很多方法都在操作address信息,就可以考虑将相关代码拆分出来

以上情况的代码,都是可以考虑将类进行拆分的。

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

当然不是。比如类的序列化和反序列化功能,如果想让类的职责更加单一,我们将其拆为一个只负责序列化工作的 Serializer 类和另一个只负责反序列化工作的 Deserializer 类。

虽然拆分之后类的职责更加单一了,但是这样增加了代码维护的难度,比如我们修改了序列化方式,从XML修改为JSON,我们需要同时对这两个类和进行修改,代码的可维护性就变差了。

updatedupdated2023-06-032023-06-03
加载评论