原创

【项目实战】去除繁琐的if..else 优雅使用策略模式

项目需求

最新开发系统权限管理系统时,有这样一个需求,不同角色的数据权限不一样需要做处理 根据数据范围拥有不同部门的数据查看权限, 比如这样

if (全部) {
获取全部部门ids
} else if (本级) {

当前用户部门id
} else if (本金以及子级) {

当前用户部门以及子部门ids
} else {

自定义的部门ids

}

复制代码

目前只是4层,虽然这样容易理解,逻辑清晰,但是虽然系统的拓展,if...else太多,这个就很可怕了。 因此使用策略模式来消除掉if else。

策略模式是一种解耦的方法,它对算法进行封装,使得算法的调用和算法本身分离。使用策略模式客户端代码不需要调整,算法之间可以互相替换,因为不同的算法实现的是同一个接口。将上面的代码优化后变为:

@Autowired
private DataScopeContext dataScopeContext;
// 根据数据权限范围查询直接通过同一个dohandler方法根据不同的tag去选择处理不同的逻辑部门ids
List<Integer> ids = dataScopeContext.getDeptIdsForDataScope(roleDto, roleDto.getDsType());
复制代码

用户在选择不同数据范围时,由context上下文进行判断选择资源去资源池调用,直接通过同一个getDeptIdsForDataScope方法根据不同的类型去选择处理不同的逻辑,从而实现结构上的优化。
具体逻辑就是:

  • 定一个策略handler接口,然后各个策略类去实现这个handler接口,并实现处理逻辑。
  • 然后定一个定义策略上下文,通过Spring将实现Strategy的实现类都自动注入到strategyMap类中,根据type获取对应的策略
代码展示:

策略接口AbstractDataScopeHandler类

/**
 * @Classname AbstractDataScopeHandler
 * @Description 创建抽象策略角色 主要作用 数据权限范围使用
 * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com
 * @Date 2019-06-08 15:45
 * @Version 1.0
 */

public interface AbstractDataScopeHandler {

    /**
     * @param roleDto
     * @param dataScopeTypeEnum
     * @return
     */
    List<Integer> getDeptIds(RoleDTO roleDto, DataScopeTypeEnum dataScopeTypeEnum);
}
复制代码

策略类实现这个策略接口(只展现一种)

package com.xd.pre.strategy;

import com.xd.pre.constant.DataScopeTypeEnum;
import com.xd.pre.domain.SysDept;
import com.xd.pre.dto.RoleDTO;
import com.xd.pre.service.ISysDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @Classname AllDataScope
 * @Description 所有
 * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com
 * @Date 2019-06-08 16:27
 * @Version 1.0
 */
@Component("1")
public class AllDataScope implements AbstractDataScopeHandler {

    @Autowired
    private ISysDeptService deptService;
    
    // 执行逻辑
    @Override
    public List<Integer> getDeptIds(RoleDTO roleDto, DataScopeTypeEnum dataScopeTypeEnum) {
        List<SysDept> sysDepts = deptService.list();
        return sysDepts.stream().map(SysDept::getDeptId).collect(Collectors.toList());
    }
}
复制代码

Service注入策略集合,根据type获取对应的策略

package com.xd.pre.strategy;

import com.xd.pre.constant.DataScopeTypeEnum;
import com.xd.pre.dto.RoleDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Classname DataScopeContext
 * @Description 创建环境角色Context:
 * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com
 * @Date 2019-06-08 16:11
 * @Version 1.0
 */
@Service
public class DataScopeContext {

    @Autowired
    private final Map<String, AbstractDataScopeHandler> strategyMap = new ConcurrentHashMap<>();

    /**
     * Component里边的1是指定其名字,这个会作为key放到strategyMap里
     * @param strategyMap
     */
    public DataScopeContext(Map<String, AbstractDataScopeHandler> strategyMap) {
        strategyMap.forEach(this.strategyMap::put);
    }

    public List<Integer> getDeptIdsForDataScope(RoleDTO roleDto, Integer type) {
        return strategyMap.get(String.valueOf(type)).getDeptIds(roleDto, DataScopeTypeEnum.valueOf(type));
    }
}
复制代码

服务层调用getDeptIdsForDataScope方法

通过这个,就可以看到通过在不同的类型获取不同部门ids,可以自动的拿到不同的资源。

使用策略模式的好处就是通过一个封装的上下文可以自由的切换不同的算法,省去多重判断,同时可以具有很好的扩展性。

当策略过多的时候就会显得很臃肿,建议大家合理的运用设计模式 具体的项目实战可以参考我的开源项目进行理解:

项目地址: gitee.com/li_haodong/…

本文目录