单一职责原则 [Simple Responsibility Principle]
定义:不要存在多于一个类导致接口变更的原因
一个类、接口、方法只负责一项职责
-
优点
- 降低类的复杂度。实现职责有清晰的定义
- 提高类的可读性。复杂性降低提高可读性
- 提高系统的可维护性。可读性提高必然容易维护
- 降低变更引起的风险。一个接口或者一个类的修改只对相关联的实现有影响、对其他的接口无影响。提高了系统的扩展性和可维护性
未按照单一职责原则设计的接口
- IUser类
package com.example.simple_responsibility.old.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IUser {
private int id;
private String username;
private String password;
private String sex;
private Integer roleId;
}
- IUserInfoService接口
package com.example.simple_responsibility.old.service;
public interface IUserInfoService {
void setUserId(String userId);
String getUserId();
void setPassword(String password);
String getPassword();
void setUserName(String userName);
String getUserName();
boolean changePassword(String password);
boolean deleteUser();
boolean addRole(int roleId);
}
这里,大家可看一下IUserInfoService
这个接口、想必很多朋友已经看出来了,这个接口不光有对业务对象,属性的操作,还有业务逻辑的方法。让接口看起来特别臃肿。如果这个时候不考虑设计原则的话,必会影响后期的维护、而且接口中的部分方法变更会导致对其他接口有影响,严重会导致线上问题。不符合单一设计原则
按照单一职责原则设计的接口
- UserInfo类(等同IUser)
package com.example.simple_responsibility.newer.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
private int id;
private String username;
private String password;
private String sex;
private Integer roleId;
}
- IUserBizService(业务逻辑接口)
package com.example.simple_responsibility.newer.service;
/**
* 业务逻辑
*/
public interface IUserBizService {
boolean changePassword(String password);
boolean deleteUserById(Integer userId);
boolean addRole(int roleId);
}
- IUserBOService(业务对象的抽象接口)
package com.example.simple_responsibility.newer.service;
/**
* 业务对象
*/
public interface IUserBOService {
void setUserId(String userId);
String getUserId();
void setPassword(String password);
String getPassword();
void setUserName(String userName);
String getUserName();
}
- UserInfoServiceImpl(实现了业务对象和业务逻辑接口)
package com.example.simple_responsibility.newer.service.impl;
import com.example.simple_responsibility.newer.service.IUserBOService;
import com.example.simple_responsibility.newer.service.IUserBizService;
import org.springframework.stereotype.Service;
@Service
public class UserInfoServiceImpl implements IUserBOService, IUserBizService {
//省略实现...
}
按单一职责改造后,代码看起来是不是更整洁了、从原来的一个接口按职责划分成业务对象、属性接口以及业务逻辑接口。
对于接口,我们再设计的时候就要考虑职责划分,尽量做到单一。对于实现类如果硬要生搬硬套可能会导致类的剧增,影响维护。不要认为制造复杂性
这里给大家简单举个例子
张三在一个小区买了A、B两套房子。交房的时候售楼处给张三两把钥匙,一把是A房的、一把是B房的。
买完之后要装修,如果这个时候张三将两栋房子都统一成一把钥匙。这样平时带一把钥匙就可以开两套房的门。
这天,张三外出办事,不小心把钥匙弄丢了,这时候张三那个家都回不去了、只能让媳妇过来送钥匙。
不过因为之前换过,所以钥匙开不开房门,只能找开门的师傅过来开门,把钥匙和锁都换成原来的了。
这就是单一职责原则,一个钥匙开一把锁,万能钥匙除外(手动滑稽)。
不知道上边这个例子大家看懂了没有,单一职责原则可以简单理解为一把钥匙开一把锁。复用到项目接口中,就是一个接口只做一件事,而且一个接口的变更不会影响其他接口,互不影响。
方法级别单一
- Method类示例
package com.example.simple_responsibility.newer.method;
import com.example.simple_responsibility.newer.domain.UserInfo;
import java.util.Map;
public class Method {
public void modifyUserInfo(String username, String address) {
username = "San";
address = "Beijing";
}
public void modifyUserInfo(String username, String address, boolean bool) {
//不建议
if (bool) {
username = "San";
} else {
address = "Shanghai";
}
}
public void modifyUserInfo(Map map) {
UserInfo username = (UserInfo) map.get("username");
//。。。 不建议、维护困难
}
public void modifyUserInfo(String username, String... fileds) {
username = "San";
for (String filed : fileds) {
//if 。。。不建议
}
}
//以下建议使用
public void modifyUserName(String username) {
username = "San";
}
public void modifyUserAddress(String address) {
address = "Shenzhen";
}
}
方法级别的设计建议符合单一职责原则,特别一些老项目或者新入职公司的小伙伴,如果有用Map
进行传参的接口抓紧更正过来吧!如果没有最新的接口文档或者老人告诉你,会影响后期维护效率的,可以将其改成DTO
等对象传参。
好了,今天单一职责原则就简单介绍到这里。如有不足敬请批评指正。
代码已更新到github上、此处只做部分演示、让大家了解单一职责原则的思想
github地址https://github.com/Ronald-Fi/Spring-Cloud
B站地址https://space.bilibili.com/374066240
欢迎大家留言评论。