• 位运算符设置权限


    巧用位运算能极大的精简代码和提高程序效率。所以,在一些优秀的开源代码中,经常能出现位运算。所以,把位运算这种思想迁移到业务代码里,有时候往往能起到柳暗花明般的重构。

    位运算

    在 JAVA 语言中,定义了诸多的位运算符,如下所示:

    运算符描述
    &
    |
    ~
    ^ 异或
    << 左移
    >> 右移

    &(与)

    十进制二进制
    3 0 0 1 1
    5 0 1 0 1
    & 后结果:1 0 0 0 1

    即:对应位都为 1 时,才为 1,否则全为 0。

    |(或)

    十进制二进制
    3 0 0 1 1
    5 0 1 0 1
    | 后结果 :7 0 1 1 1

    即:对应位只要有 1 时,即为 1,否则全为 0。

    ~(非)

    十进制二进制
    3 0 0 1 1
    ~ 后结果:12 1 1 0 0

    即:对应位取反。

    异或 ^

    十进制二进制
    3 0 0 1 1
    5 0 1 0 1
    ^ 后结果:6 0 1 1 0

    即:只要对应为不同即为 1。

     

    使用位运算重构项目

    当前我们需要设计一个权限模块,可动态的为用户指定某个文件的操作权限。并且,用户对一个文件的操作权限分为:读(R),写(W),执行(X)。

    这是一个很简单的需求,为了描述这种关系,我们会在数据库表关系设计时,定义如下的结构:

    数据表:user_file_permission

    字段类型备注
    userId int 用户
    fileId int 文件
    readable bit 是否可读
    writable bit 是否可写
    executable bit 是否可执行

    映射的模型:UserFilePermission

    public class UserFilePermission {
        /**
         * 用户
         */
        private User user;
    
        /**
         * 文件
         */
        private File file;
    
        /**
         * 读操作
         */
        private Boolean readable;
        /**
         * 写操作
         */
        private Boolean writable;
        /**
         * 执行操作
         */
        private Boolean executable;
    }
    
    

    这是常见的实现方式。但考虑下,业务需求千变万化,倘若需要再新增一个下载(D) 操作,是不是需要去额外扩展一个字段。所以,对于长期来讲,有值得重构的空间。

    故缺点很明显:

    • 难扩展
    • 繁琐,比如判断是否包含读和执行的操作权限,需要这样写if(xx.IsReadable() && xx.IsExecutable()),但随着权限操作越来越多时,if代码块也越来越大。

    位运算重构

    了解 Linux 的同学一定知道利用 chmod 来控制文件如何被他人调用。比如针对一个文件,可分别给 User、Group、Other 设置访问的权限。同时权限操作分为:r(读),w(写),x(执行)。很巧,和我们的需求一样。那我们来看下Linux 是如何实现权限控制的。

    核心是定义一个整数来代表操作权限,即:r=4,w=2,x=1

    • 若要 rwx 权限,则:4+2+1=7;
    • 若要 rw- 权限,则:4+2=6;
    • 若要 r-x 权限,则:4+1=5。

    所以使用 chmod 也可以用数字来表示权限,如下即给 User、Group、Other 三个维度的对象都设置了代表可读、可写、可执行的权限,代号:7。

    chmod 777 file
    

    你可能会想,为什么 r=4,w=2,x=1?聪明的你,肯定想到了——二进制。

    权限操作二进制十进制
    r 0100 4
    w 0010 2
    x 0001 1

    所以借由这个思想,我们对代码进行重构,去掉了readablewritableexecutable 这三个字段,而统一由一个 permissoin 字段来表示,如下所示:

    public class UserFilePermission {
    
        /**
         * 可执行(x):0001
         */
        public static final int OP_EXECUTABLE = 1;
    
        /**
         * 可写(w):左移一位:0010
         */
        public static final int OP_WRITABLE = 1 << 1;
    
        /**
         * 可读(r):左移二位:0100
         */
        public static final int OP_READABLE = 1 << 2;
    
        /**
         * 用户
         */
        private User user;
        /**
         * 文件
         */
        private File file;
    
        /**
         * 权限
         */
        private int permission;
    }
    
    

    其中 permission 的可选项如下表格所示:

    permissionrwx描述
    1(0001) 0 0 1 可执行
    2(0010) 0 1 0 可写
    4(0100) 1 0 0 可读
    3(0011) 0 1 1 可写、可执行
    7(0111) 1 1 1 可读、可写、可执行
    0(0000) 0 0 0 禁止

    同时,操作权限不是一尘不变的,我们往往需要对其新增、删除、查询。通过位运算,可以非常方便实现。

    为当前权限新增一个操作:

    public void addOp(int op) {
        permission |= op;
    }
    

    为当前权限删除一个操作:

    public void removeOp(int op) {
        permission &= ~op;
    }
    

    判断当前权限是否包含指定的操作权限:

    public boolean containsOp(int op) {
        return (permission & op) == op;
    }
    

    判断当前权限是否不包含指定的操作权限:

    public boolean notContainsOp(int op) {
        return (permission & op) == 0;
    }
    

    当然,这样的重构唯一的缺点就是可读性变差。当然,如果团队对位运算达成共识之后,大家都有一定的了解。相反,可读性还是可以的。同时,位运算的计算非常快,也在一定程度上提升了执行效率。

  • 相关阅读:
    被.net郁闷的一天
    使用批处理出现奇怪的现象
    我们应该更相信ghost
    asp.net设置默认按钮的一种方法(041217更新)
    asp中access到sql server导入升级后要做的工作。
    一种简单方便的权限控制方案
    为何我的本本不能打开休眠功能?
    祝贺自己的blog开张
    sql server中分页获取数据的存储过程
    httpcompress实际效果能有多少?
  • 原文地址:https://www.cnblogs.com/sq1995liu/p/14293346.html
Copyright © 2020-2023  润新知