七大设计原则之单一职责原则
目录
一、什么是单一职责原则?
二、如何判断是否满足单一职责原则?
三、单一职责原则是不是越单一就越好?
一、什么是单一职责原则?
单一职责原则的英文名叫Single Responsibility Principle。所以,单一职责原则也简称SRP原则。这个原则相对其它原则是更好理解的,其实单从名字也是可以看出来这个原则是干啥的。我们先来看下单一职责原则的英文定义,A class or module should have a single responsibility。翻译过来就是,一个类或者模块应该只负责一个职责。所以说这个原则不仅是针对类的,也是针对模块的。其实往更大了说也可以针对一个服务,在进行服务拆分的时候,我们也要保证每个服务的职责单一,这样才能达到解耦的作用,也就可以提高可维护性。往更小了说,类中的方法也要尽可能的满足单一职责,从而使代码有更强的复用性以及可维护性。比如,我们定义一个方法handleUserAction用于处理用户的注册、登录、登出操作。
public void handleUserAction(String action, String username, String password) {
if ("register".equals(action)) {
// 注册用户的逻辑
System.out.println("Registering user: " + username);
} else if ("login".equals(action)) {
// 用户登录的逻辑
System.out.println("Logging in user: " + username);
} else if ("logout".equals(action)) {
// 用户注销的逻辑
System.out.println("Logging out user: " + username);
}
}
显然上面这种定义的方式,明显不符合单一职责原则。这种代码大概有如下几个缺点:
- 可维护性差。假设其中注册用户的逻辑需要更改的话,就有可能会影响其它的逻辑,也就是降低了代码的可维护性。
- 可读性差。这种方法里面有大量的if else也会降低代码的可读性,其实间接的也影响了可维护性。
- 可复用性差。从上面的例子,我们好像看不出复用性差。因为你不管注册、登录、登出都可以使用该方法。但是,如果我们直接把生成密码的逻辑写在注册用户里面。这样,如果后面,我们在重置密码的时候,只是想使用这个生成密码的逻辑,不就无法复用了!
总之,我们在编写代码时,务必避免编写大而全的代码。无论是小到方法,还是大到一个服务,都应尽量保证其职责单一。这样写出来的代码才具有更好的可维护性、可扩展性、可复用性和可读性。这样的代码才是满足高内聚、低耦合的高质量代码!
二、如何判断是否满足单一职责原则?
在某些情况下,我们可以直接判断一个类或方法是否满足单一职责原则(Single Responsibility Principle, SRP)。例如,在之前提到的例子中,如果一个方法同时处理用户注册、登录和登出逻辑,同时还涉及密码生成等功能,显然这些职责之间存在耦合,违反了SRP。同样的,如果将完全不相关的类如用户类和订单类定义在同一个类中,这显然是不符合单一职责原则的。因为用户类应该只关注用户相关的操作,而订单类应该只关注订单相关的操作,两者的职责应该被明确分离。当然,有些场景下判断一个类是否满足SRP确实比较困难。这里举个经典的例子:用户类。相信每位写过代码的人肯定都定义过这个类。
@Data
public class User {
private long id;
private String name;
private String email;
private String telephone;
private Date createTime;
private Date updateTime;
private Date lastLoginTime;
private String avatarUrl;
private String provinceOfAddress;
private String cityOfAddress;
private String regionOfAddress;
private String detailedAddress;
}
-
从用户层面看:
UserInfo
类包含所有与用户相关的信息,包括基本的用户信息(如用户名、电子邮件、电话号码等)和地址信息。从这个角度看,该类似乎满足单一职责原则,因为它处理的是用户信息。 -
从更细粒度的业务层面看:如果将用户信息分为基本用户信息(如用户名、电子邮件、电话号码等)和地址信息,那么
UserInfo
类实际上承担了两个不同的职责:管理用户基本信息和管理用户地址信息。这种情况下,可以认为它不完全符合单一职责原则。
那么,这个类到底满不满足单一职责原则呢?
其实,对于这种类来判断到底满不满足SRP,就需要结合具体的业务场景了。如果说,在一个社交网络中,用户的地址只是用于展示,那么可以将用户基本信息和地址信息放在同一个类中,因为地址信息的比重较小。如果在电商平台中,用户的地址信息需要用于订单和物流处理,那么地址信息的比重较大,应该将地址信息拆分成独立的类,如 UserAddress
,以提高类的内聚性和降低耦合性。
三、单一职责原则是不是越单一就越好?
显然不是,任何事情都是过犹不及,对于单一职责原则也是,并不是职责越单一就越好。如果过度拆分,会导致系统中存在大量的小类,从而增加了系统的复杂性和管理成本,并且降低了代码的聚合性。如果在设计阶段一直追求极致的单一职责,就会分散更多的精力在这上面。此外,还有一种可能性是,你认为已经达到了单一职责,但随着业务的发展,这个职责又会变得不再单一。
实际上,在真正的软件开发中,我们并不需要过于未雨绸缪,过度设计。因此,我们可以先写一个粗粒度的类来满足目前得业务需求就行了。随着业务的不断发展,如果这个粗粒度的类变得越来越庞大,代码量越来越多,这时我们就可以将可以将其拆分成几个更细粒度的类。这也就是我们常说的持续重构。
补充:
判断一个类不满足单一职责原则的方法:
- 类中的代码行数、方法或者属性过多;
- 类依赖的其他类过多,或者依赖类的其他类过多;
- 私有方法过多;
- 比较难给类起一个合适的名字;
- 类中大量的方法都是集中操作类中的某几个属性。