Pulpcode

捕获,搅碎,拼接,吞咽

0%

使用位掩码来表示多个职位

今天OA系统需要修改需求,之前的一个人员只能有一个职位的实现不能满足需求,要改为一个人可以同时具有多个职位。
之前职员的post的职位是使用enum实现的:

1
2
3
4
5
6
7
public Enum Post{
Keyuan,/*科员*/
Fuzhuren,/*副主任*/
Zhuren,/*主任*/
Fuyuanzhang,/*副院长*/
Yuanzhang/*院长*/
};

新需求将添加与项目相关的职位,例如组长,总共。类似”副院长”同时又是”总工”。
其数据库底层是使用int类型的变量进行存储的。

虽然说enum一次只能有一个值,但是还是能通过一些“技巧”来实现一个人多种职位,那就是位掩码。

在维基百科上,对位掩码的定义是这样的:
“在计算机学科及数字逻辑中指的是一串二进制数字,通过与目标数字的按位操作,达到屏蔽指定位而实现需求”
比如对于一个八位二进制数:
00101110
我想要它的前四位,那么只需要这样与掩码进行与运算即可:

00101110&11110000 = 00100000

这其实就像是linux的文件权限一样。“755”
因为是位的运算,你一定可以肯定7表示4+2+1,你也可以通过7&4的结果是不是0,来判断是否包含这一位。
所以我们将使用2^n来重新实现Enum Post:

0001
0010
0100
1000

好吧,下面是新版本的(加入组长和总共)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Enum PostType{
Keyuan = 1,
/*科员*/
ZuZhang = 2,
/*组长*/
Fuzhuren = 4,
/*副主任*/
Zhuren = 8,
/*主任*/
Fuyuanzhang = 16,
/*副院长*/
ZongGong = 32,
/*总工*/
Yuanzhang = 64,
/*院长*/
};

那么就可以这样为其赋值:

1
2
3
4
5
6
7
8
9
10
11
12
    Post = int(PostType.Keyuan + PostType.ZuZhang)
{% endhighlight %}

在为Employee添加一个方法,判断是否包含此职位。

{% highlight c# linenos %}
public bool HasPost(PostType post){
if(Post & post != 0){
return True;
}
return False;
}

显然这种写法显得太蠢了,优化一下:

1
2
3
public bool HasPost(PostType post){
return (this.Post & post) != 0
}

这里有一个小细节很有趣,如果是做过嵌入式的人一定会用这种写法,而不是Post & post == post.
因为对于于运算,判断是否为0,要比判断相等快很多,因为就算是判断相等,也要先用减法,再判断是否为零。

这样我们就可以为一个员工添加多种Post了。

后记

其实这里还发生了一些有趣的事。
因为要对数据库中职位原来的字段进行更新替换,所以我一开始写下一张替换表,对数据库直接更新。

1 --> 1
2 --> 2
3 --> 4
4 --> 8
5 --> 16
6 --> 32
7 --> 64

但是如果直接通过这张表用 update 语句去更新数据库是有问题的。
因为在你执行 update set post = 8 where post = 4 时,除了旧的4,还有一部分新4,这时所有的4就都被替换了。
同理还有旧8和新8。

一个比较巧妙的做法是将这些sql语句倒着写。