我们在开发的过程中会遇到这种场景,在系统的权限、选项设置,只有两种状态开启或者关闭。
具体我们以哔哩哔哩的隐私设置为例:
这么几种开关你会怎么设计数据表?按照正常的操作针对一个开关添加字段进行表示。可是后续就会有下面的这样的情况:
哈哈,产品需求迭代增加了后续几种开关,你准备怎么做?还是继续增加字段进行保存么?增加字段进行保存也是可以的,但是感觉上会有那么一点点low!新的开关出现增加字段进行保存,既要修改表结构,又要改实体属性,还是比较麻烦的!所以,对于这种类似的需求,我们要有一个良好的设计来应对,那我们如何解决呢?
用过linux系统的同学会知道这样一个知识点:
1 | chomd 777 something |
上面的linux命令代表把something的权限修改成所有者、用户组、其他用户都可以进行可读、可写、可执行。
数字 | 权限 | rwx | 二进制 |
---|---|---|---|
7 | 读 + 写 + 执行 | rwx | 111 |
6 | 读 + 写 | rw- | 110 |
5 | 读 + 执行 | r-x | 101 |
4 | 只读 | r– | 100 |
3 | 写 + 执行 | -wx | 011 |
2 | 只写 | -w- | 010 |
1 | 只执行 | –x | 001 |
0 | 无 | — | 000 |
用的是二进制的001代表可执行,010代表可写,100代表可读,那么只需要3位二进制数即可表示这三种状态的混合搭配。借用这种思想,我们来解决上述的问题,我们用一个int类型字段就可以表示上述所有的状态开关。使用2的次幂值代表一种状态,比如我们用
2的零次方 $2^{0}$ = 1 表示打开我的收藏
2的一次方 $2^{1}$ = 2 表示打开追番追剧
2的二次方 $2^{2}$ = 4 表示打开订阅标签
2的三次方 $2^{3}$ = 8 表示打开最近投币的视频
2的四次方 $2^{4}$ = 16 表示打开个人资料
2的五次方 $2^{5}$ = 32 表示打开最近玩过的游戏
博客使用hexo搭建,采用默认的landscape主题不支持LaTeX 公式,所以上面的次方公式显示的有问题。
二进制 | 数字(十进制) | 含义 |
---|---|---|
000 000 | 0 | 全部关闭 |
000 001 | 1 | 开启我的收藏 |
000 011 | 3 | 开启我的收藏、追番追剧 |
000 111 | 7 | 开启我的收藏、追番追剧、订阅标签 |
111 111 | 63 | 全部开启 |
其实就是用bit位表示开启或者关闭,如果是1表示开启,0表示关闭。
1 | 或运算:bit位上有1为1。例如:2 | 1 == 0000 0010 | 0000 0001 == 3 |
那我们就来编码实现上述功能:
1 | package com.ydstudio.flashsale.module.business.bit; |
开启我的收藏和最近玩的游戏,其他选项都关闭,则计算过程如下:
1 | # 我的收藏 tag = 1 最近玩的游戏 tag = 32 |
关闭最近玩的游戏,则计算过程如下:
1 | 上面计算的tags = 33 ,最近玩的游戏 tag = 32 |
判断是否开启最近玩的游戏,则计算过程如下:
1 | 上面计算的tags = 33 ,最近玩的游戏 tag = 32 |
如何用SQL查询开启最近玩的游戏选项的用户
1 | # MySQL也是支持位运算的,其他的数据库应该也是支持的 |
就这样我们就可以用一个int型字段保存4*8 = 32个状态位(无符号的情况下),看起来还是很不错,运算的效率高,只用一个字段可以保存较多的状态。缺点嘛也是有的,就是可读性差,不能很直观的看出到底有哪些状态。其实这种方法可以用一个字段表示多种状态,例如 订单一般有支付状态:待支付 1 、支付中 2 、支付成功4 、支付失败8和发货状态:待发货16、已发货32、在途64、已收货128,这样的话用一个orderStatus来表示这两种状态:支付成功4 + 待发货16 = 4 | 16 = 20,也是可以的但是如上面所说,优缺点都有,看你的实际情况进行选择了!