状态码设计的智慧,1234还是1248
Tag 状态码, 设计, on by view 3508

一个事物有5个流程,ABCDE一般的程序员通常会定义为 status 有5个状态 0/1/2/3/4 ,流程的顺序是 A->B->C->D->E ,可是有一天项目经理说这个流程会有个快捷流程 A->B->E 。于是原本的 0->1->2->3->4 就有部分数据变为了 0->1->4 ,这些都不会有太大的问题,然而,后来项目经理跟你说我需要区分在E状态下它是经历了 ABCDE 还是直接经历 ABE ,于是乎,你就懵逼了。

有另外一种设计方式倒是可以获取详细的经历的流程结点,那就是 A:1, B:2, C:4, D:8, E:16 。于是A->B->C->D->E 表示的状态码切换过程为 1->3->7->15->31 。A->B->E 表示的状态码切换过程为 1->3->20, 有人说,你这样当处于E状态时就有可能有两个状态码了31和20,我如何判定他是处于E状态呢,其实这是位运算上的一个小技巧,我们可以很简单的判定出他是否在E状态 status & 0x10 > 0 若计算结果为true,则表示有经历E状态,按照你的流程方向便可以判定是处于哪一未置

switch (true) {
	case status & 0x10:
		return "E";
	case status & 0x08:
		return "D";
	case status & 0x04:
		return "C";
	case status & 0x02:
		return "B";
	case status & 0x01:
		return "A";
}

这一设计方式在ERP等系统的权限控制中更是常见

例如某一项目下有10个操作不同的用户可以配置不同的操作权限,那么就有 0b0000000000 到 0b1111111111 ,呃……,1024种权限配置方式,如果是100个【weightValue.size()】操作,那么就有 2^100 种权限配置方式。我们只需要给该用户一个 int(11) 类型的字段就可以完成权限配置。

又例如,某简历需要计算简历完整度,简历中分为10个【weightValue.size()】模块,不同的模块占分权值不同,那么我们也可以用一个字段来标记该简历的各模块的完成状况,0b0000000000 到 0b1111111111 ,你可以这样计算分值

int value = 0;
int weightValueNumber = weightValue.size();
int zerokeep = math.pow(2, weightValueNumber);
status = status + zerokeep;
for (int i = 0; statusMove - zerokeep > 0; i++) {
	statusMove = status < (i + 2); // 左移 (i+1)+1 位
	int nakedValue = (statusMove > (i + 2)) ^ status - zerokeep;
	boolean checked = nakedValue > 0;
	if (checked) {
		value += weightValue.get(i);
	}
	status = nakedValue + zerokeep;
}
return value;

这种算法的始祖应该算是 Linux 系统上的权限管理机制吧,chmod 777 /xxx 这种操作后面的智慧便是如此。若是铁定的不涉及到取某个结点ABCDE的独立状态详情,那么你可以随意定义状态码 1/2/3/4/5 或是 -2/-1/0/1/2 随你所好,但是假如某一天你遇上了坑爹的项目经理突然告诉你需要知道是否经历了CD状态,那么你在懵逼的时候是不是也会想起Linux系统上 chmod 指令的智慧呢?1234还是1248取决于你最初的设计。