php多态计算

yuyu888 于 2020-12-22 发布

话不多说直接放代码

class  DataMultiplex
{

    public function get_is_attribute($x_rank, $attribute)
    {
        return ($x_rank&$attribute)==$attribute;
    }
    public function set_add_attribute($x_rank, $attribute)
    {
        return ($x_rank | $attribute);
    }

    public function set_del_attribute($x_rank, $attribute)
    {
        $_temp = ~$attribute;
        return ($x_rank & $_temp);
    }

    // 检查是否是2的n次方
    public function check_attribute($attribute){
        return !($attribute&($attribute-1));
    }

    // 属性遍历
    public function get_attribute_list($x_rank){
        $list = array();
		$n=1;
        while($x_rank>0){
            // &表示按位与,0x开头的表示的是十六进制数,k&0x1表示k与0x1按位与,其效果为取k的二进制中最右边的数字
			if($x_rank&0x1){ 
				$list[] = $n;
			}
			$n = $n<<1;
            $x_rank = $x_rank>>1;
        }
        return $list;
    }

    // 遍历2 更优方案
    public function get_attribute_list2($x_rank){
        $list = array();
        while($x_rank != 0){
            $list[] = $x_rank-($x_rank & ($x_rank-1));
            $x_rank = $x_rank & ($x_rank-1);
        }
        return $list;
    }

}

说明

比如有一个账单处理流程, 需要主管审批,经理审批,财务审批,出纳付款

通常数据表会设计成这样

主管审批(是/否) 经理审批(是/否) 财务审批(是/否) 出纳付款(是否)
0 0 0 0

假如该条数据 主管已审批 且 经理已审批

主管审批(是/否) 经理审批(是/否) 财务审批(是/否) 出纳付款(是否)
1 1 0 0

假如要在中间加上总裁审批, 财务总监审批, 免不了又要加字段, 当需求有变化的时候不免显得成本有点高,而且多个字段意味着要多开辟一个存储空间,当数据量大的时候,存储开销也会更大

我们仔细看下 1100 看起来是不是有点感觉了,为了方便理解反转一下 0011

把状态做如下定义:主管已审批:1; 经理已审批:2; 财务已审批:4; 出纳已付完款:8

对于主管已审批 且 经理已审批的 结果就是3, 转成二进制就是 11

对应关系表

数据库字段值 二进制表示 出纳付款(8) 财务审批(4) 经理审批(2) 主管审批(1)
0 0000 N N N N
1 0001 N N N Y
2 0010 N N Y N
3 0011 N N Y Y
4 0100 N Y N N
5 0101 N Y N Y
6 0110 N Y Y N
7 0111 N Y Y Y
15 1111 Y Y Y Y

是不是豁然开朗了, 我只用一个字段就可以把所有状态存储清楚了, 而且很容易扩展, 原来需要新增一个字段来表示新的业务类型,现在只用新增一个业务类型的定义就好了,值就是2的n次方

mysql里 一个int 是4个字节, 一个字节8位;所以可以表达32个bit位;所以如果值开辟了一个字段来存储属性, 最多可以设置32个属性;超过了需要自行扩展

这不仅仅是一个类, 更是一种系统设计思想, 大家可以发挥想象力, 看自己的系统能不能用上

如何查询

查含有4的属性集合

select * from reimbursement where attribute&4;

查含有1和2 的属性集合

select * from reimbursement where attribute&3=3

添加属性

UPDATE reimbursement SET attribute=attribute|8 where id=6; 

删除属性

UPDATE reimbursement SET attribut=attribute^8 where id=6; //删除一个不存在于该属性集合的值时会出错
UPDATE reimbursement SET attribute=(attribute & ~8) where id=6; // 正确

当然使用了位运算就不能用索引

结语

这个其实就是个 bitmap 的典型应用