目录
1.背景
想起以前项目中有权限判断,决定用户当前能执行什么操作,一大堆if-else,多一个角色就多一个if-else,虽然领导说这个需求不会变,但谁知道呢,我们要保证扩展性和易读性。
2.需求
一个工作流相关的问题,但前提是不使用工作流框架,领导说比较简单的审批流程而且不会再变动了,用几个表实现+标识就可以实现,意思是XX角色在XX环节应该有什么权限个操作。
3.思考
看着一大堆if-else,再来一个角色就多一个if,易读性极差,而且逻辑太多了,很多判断嵌套,但是最终都是输出一个/系列参数或者执行一个方法。
4.旧代码if-else
为了更好的理解,我就在controller写出来,不在serviceImpl写了,节省大家理解时间。
角色一共分3种,安保、老师、学生,每个角色都会说他们身份的话,对应都是speak()方法。根据入参roleCode角色编号,判断是哪个角色,应该返回他们所说的话。
安保:我是安保人员,我可以保卫老师和学生们。
老师:我是老师,我很好地管理我的班级。
学生:我是学生,我已经完成我的作业了。
@RequestMapping("/home")
@RestController
public class HomeController {
// 旧代码-if-else
@RequestMapping("/strategyBefore")
public String strategyBefore(Integer roleCode) {
switch (roleCode) {
case 1: // 安保
return "I am a security, I can protect teachers and students";
case 2: // 老师
return "I am a teacher, I adjusted really well to my class.";
case 3: // 学生
return "I am a student, I have completed my homework";
default:
throw new RuntimeException("Can not find the role") ;
}
}
}
5.新代码-策略模式
采用简单工厂模式+策略模式,1个枚举+1个接口+1个工厂+n个策略实现类,迫切需要解决的就是判断问题。
Controller:新代码
@RequestMapping("/home")
@RestController
public class HomeController {
// 新代码-策略模式
@RequestMapping("/strategyAfter")
public String strategyAfter(Integer roleCode) {
IRole role = RoleFactory.getRole(roleCode);
return role.speak();
}
}
枚举类:用在工厂类里,利用hashMap的key-value特性和get()方法解决了判断值问题。
/**
* @author chengqinhong
* @date 2022/12/12 11:16
* @description 角色权限枚举
*/
public enum RoleEnum {
SECURITY(1, "security"), // 安保
TEACHER(2, "teacher"), // 老师
STUDENT(3, "student"); // 学生
private final int code;
private final String msg;
RoleEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
接口类:声明一个方法,各个实现类都实现该方法,但是不同的类该方法的逻辑代码不一致。
/**
* @author chengqinhong
* @date 2022/12/11 22:11
* @description 策略模式定义的接口
*/
public interface IRole {
// 角色权限查询返回接口
String speak();
}
工厂类:获取对象
/**
* @author chengqinhong
* @date 2022/12/12 11:21
* @description 获取角色对象工厂
*/
public class RoleFactory {
public static Map<Integer, IRole> roleMap = new HashMap<>();
// 利用key-value形式区分不同的角色对应的的实体类
static {
roleMap.put(RoleEnum.SECURITY.getCode(), new SecurityImpl());
roleMap.put(RoleEnum.TEACHER.getCode(), new TeacherImpl());
roleMap.put(RoleEnum.STUDENT.getCode(), new StudentImpl());
}
public static IRole getRole(Integer roleCode) {
IRole role = roleMap.get(roleCode);
if (role == null) {
throw new RuntimeException("Can not find the role");
}
return role;
}
}
策略实现类:不同角色对该方法都有不同的逻辑
角色- 安保
import com.example.demoWeb.service.IRole;
/**
* @author chengqinhong
* @date 2022/12/11 22:13
* @description 角色-安保
*/
public class SecurityImpl implements IRole {
@Override
public String speak() {
return "I am a security, I can protect teachers and students";
}
}
角色-老师
import com.example.demoWeb.service.IRole;
/**
* @author chengqinhong
* @date 2022/12/11 22:13
* @description 角色-老师
*/
public class TeacherImpl implements IRole {
@Override
public String speak() {
return "I am a teacher, I adjusted really well to my class.";
}
}
角色-学生
import com.example.demoWeb.service.IRole;
/**
* @author chengqinhong
* @date 2022/12/11 22:13
* @description 角色-学生
*/
public class StudentImpl implements IRole {
@Override
public String speak() {
return "I am a student, I have completed my homework";
}
}
新旧代码对比 :新代码-策略模式的易读性完胜旧代码-if-else
@RequestMapping("/home")
@RestController
public class HomeController {
// 新代码-策略模式
@RequestMapping("/strategyAfter")
public String strategyAfter(Integer roleCode) {
IRole role = RoleFactory.getRole(roleCode);
return role.speak();
}
// 旧代码-if-else
@RequestMapping("/strategyBefore")
public String strategyBefore(Integer roleCode) {
switch (roleCode) {
case 1: // 安保
return "I am a security, I can protect teachers and students";
case 2: // 老师
return "I am a teacher, I adjusted really well to my class.";
case 3: // 学生
return "I am a student, I have completed my homework";
default:
throw new RuntimeException("Can not find the role") ;
}
}
}
6.总结
从整个项目开发的角度和维护的角度来说,有很大帮助!
从代码的易读性来说,有很大的提高!
从代码数来说,代码量膨胀了好几倍...
从软件的设计难度来说,有一点点提高。
注意其中有简单工厂模式则不可避免的是‘开-闭 原则’,不好实现扩展性,因为每次新增模式都要修改原来的简单工厂类。
从个人的设计水平来说,是有很大的提高,这个设计模式带来的思考是很不一样的,不再是死亡if-else,自己写的头疼,后人接手也看不明白,需求有一点点逻辑变动就要改代码重新构建发布等等。该模式结合数据库维护实现可配置/接口进行维护/后台管理已有的策略,有变更的需求可以简单操就达到效果,提高了灵活性。