二進(jìn)制在互聯(lián)網(wǎng)業(yè)務(wù)開發(fā)中的精妙應(yīng)用
背景
在互聯(lián)網(wǎng)業(yè)務(wù)開發(fā)中,我們經(jīng)常有這樣的一個場景,比如有一個活動,這個活動可以是一個紅包的活動,也可以是一個優(yōu)惠券活動,也可以是一個秒殺活動等等,要求這個活動只能在某些場景下使用。
例子:
- 如京東上面有很多優(yōu)惠券,
- 有的優(yōu)惠券只能在京東上使用,
- 有的優(yōu)惠券只能在京東7Fresh上使用,
- 有的優(yōu)惠券只能在京東、京東7Fresh上使用。
我們立馬能想到一個方案,就是新增一個活動類型字段activityType,通過這個字段來區(qū)分這些活動。
那么如何統(tǒng)一、高效、可擴(kuò)展地存儲這個活動標(biāo)識,以便后續(xù)通過這個標(biāo)識,來判斷這個活動能否在特定的某些場景中使用呢?
方案一
我們可以通過枚舉實現(xiàn),每個枚舉包含2個屬性——標(biāo)識、場景,通過標(biāo)識來判斷活動是否能在該場景中使用。
1.存儲標(biāo)識
如只能在京東上使用的標(biāo)識為1,只能在淘寶特價版上使用的標(biāo)識為3,等等。
package com.example.activitytype.constants;
/**
* 活動類型枚舉
*
* @author hongcunlin
*/
public enum ActivityType {
/**
* 京東
*/
JD(1, "京東"),
/**
* 京東極速版
*/
JDJSB(2, "京東極速版本"),
/**
* 京東極速版
*/
JD_JDJSB(3, "京東、京東極速版本");
/**
* 標(biāo)識
*/
public Integer code;
/**
* 場景
*/
public String desc;
/**
* 枚舉
*
* @param code 標(biāo)識
* @param desc 場景
*/
ActivityType(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}
枚舉里邊列舉幾個活動的類型對應(yīng)的標(biāo)識,當(dāng)然這個標(biāo)識的數(shù)據(jù)類型,也可以為字符串,這里簡單采用整數(shù)來實現(xiàn)。
2.判斷使用
上面是基礎(chǔ),都是為下面的服務(wù)提供數(shù)據(jù)支撐而已,下面的服務(wù)才是我們要直面的業(yè)務(wù),如判斷這個活動能否在京東極速版上使用,我們的實現(xiàn)方案對應(yīng)為:
package com.example.activitytype.service.impl;
import com.example.activitytype.constants.ActivityType;
import com.example.activitytype.service.ActivityTypeService;
import org.springframework.stereotype.Service;
/**
* 活動服務(wù)
*
* @author hongcunlin
*/
@Service
public class ActivityTypeServiceImpl implements ActivityTypeService {
/**
* 能否在京東極速版使用
*
* @param code 標(biāo)識
* @return true能/false不能
*/
@Override
public boolean isCanUseInJdjsb(Integer code) {
return ActivityType.JDJSB.code.equals(code) ||
ActivityType.JD_JDJSB.code.equals(code);
// TODO 后續(xù)這里需要不斷維護(hù)
}
}
3.代碼測試
測試結(jié)果和我們預(yù)期的一樣
package com.example.activitytype;
import com.example.activitytype.service.ActivityTypeService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class ActivityTypeTest {
/**
* 活動服務(wù)
*/
@Resource
private ActivityTypeService activityTypeService;
/**
* 能否在京東極速版使用測試
*/
@Test
void isCanUseInJdjsbTest() {
// 1,京東,false
System.out.println(activityTypeService.isCanUseInJdjsb(1));
// 2,京東極速版本,true
System.out.println(activityTypeService.isCanUseInJdjsb(2));
// 3,京東、京東極速版本,true
System.out.println(activityTypeService.isCanUseInJdjsb(3));
}
}
4.評價
這個方案的優(yōu)點就是前期實現(xiàn)起來很簡單。
缺點就是隨著維護(hù)的活動類型越來越多,我們需要補(bǔ)充很多判斷語句,并且每新增1種活動類型我們需要把涉及的方法都改一遍,這對我們開發(fā)人員來說,是完全不可接受的。
方案二
我們可以通過二進(jìn)制字符串01來存儲這些場景,0代表不能使用,1代表能使用,后續(xù)我們通過判斷二進(jìn)制字符串的某個位置的值是否為1,就可以輕松判斷是否能在這個場景使用了。
1.存儲位置
我們的枚舉,由一個籠統(tǒng)標(biāo)識,改為存儲二進(jìn)制的位置。
package com.example.activitytype.constants;
/**
* 活動類型枚舉
*
* @author hongcunlin
*/
public enum ActivityIndex {
/**
* 001
* 京東
*/
JD(1, "京東"),
/**
* 010
* 京東極速版
*/
JDJSB(2, "京東極速版本"),
/**
* 100
* 京東七鮮
*/
JD7F(4, "7Fresh");
/**
* 位置
*/
public Integer index;
/**
* 場景
*/
public String desc;
/**
* 枚舉
*
* @param index 位置
* @param desc 場景
*/
ActivityIndex(Integer index, String desc) {
this.index = index;
this.desc = desc;
}
}
如下圖所示,只能能在京東極速版使用的標(biāo)識,我們存儲為2(二進(jìn)制為010),通過判斷第2位是否為1,就可以判斷是否只能在京東極速版使用了。
2.判斷使用
根據(jù)所在位為1,即可判斷能否在該場景下使用
package com.example.activitytype.service.impl;
import com.example.activitytype.constants.ActivityIndex;
import com.example.activitytype.service.ActivityIndexService;
import org.springframework.stereotype.Service;
/**
* 活動服務(wù)
*
* @author hongcunlin
*/
@Service
public class ActivityIndexServiceImpl implements ActivityIndexService {
/**
* 能否在京東極速版使用
*
* @param code 標(biāo)識
* @return true能/false不能
*/
@Override
public boolean isCanUseInJdjsb(Integer code) {
// 使用 位運(yùn)算 判斷 值的二進(jìn)制 指定位是否為1
return (code & ActivityIndex.JDJSB.index) != 0;
}
}
這里采用了位運(yùn)算,簡單高效。
3.代碼測試
代碼的運(yùn)行結(jié)果,和我們預(yù)期的一樣
package com.example.activitytype;
import com.example.activitytype.service.ActivityIndexService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class ActivityIndexTest {
/**
* 活動服務(wù)
*/
@Resource
private ActivityIndexService activityIndexService;
/**
* 能否在京東極速版使用測試
*/
@Test
void isCanUseInJdjsbTest() {
// 010,true
System.out.println(activityIndexService.isCanUseInJdjsb(2));
// 111,true
System.out.println(activityIndexService.isCanUseInJdjsb(7));
// 100,false
System.out.println(activityIndexService.isCanUseInJdjsb(4));
// 001,false
System.out.println(activityIndexService.isCanUseInJdjsb(1));
}
}
4.評價
我們枚舉的存儲,不像方案一中存在冗余的特征,如JDJSB、JD_JDJSB存在交集JDJSB,這是不符合編程中的OOP思想的。
此外,我們通過位運(yùn)算判斷,速度更快,也就是說性能更好。
最后,我們的代碼后續(xù)隨著活動類型的新增,無需開發(fā),也就是維護(hù)性更好。
最后
本文中的案例代碼已經(jīng)上傳到github了,有需要同學(xué)可以自行下載