前提

某一天巧合打开了sofa-bolt项目,查找部分源码,看到了项目中使用bit数组实现功能开关的特性,感觉这种方式可以借鉴,于是写下这篇文章。

原理

bit数组的布局如下:

由于每个bit都可以表示1或者0,刚好对应于开关的ONOFF。只需要定义好每个开关所在的bit数组下标和开关的状态(ON = 1或者OFF = 0),通过判断不同开关下标所在的bit即可判断开关的状态:

  • 优点:节省空间,理论上只需要占用最多2n位的内存(n为开关的数量,这里考虑扩容,扩容让bit数组长度为原来的2倍)
  • 缺点:暂时没发现

实现

JDK中的bit数组可以直接使用BitSet。首先定义开关定义SwitchDef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class SwitchConst {

public static final boolean ON = true;
public static final boolean OFF = false;
}

@RequiredArgsConstructor
@Getter
public enum SwitchDef {

/**
* 启用HTTPS
*/
ENABLE_HTTPS(0, SwitchConst.ON, "启用HTTPS"),

/**
* 启用异步
*/
ENABLE_ASYNC(1, SwitchConst.OFF, "启用异步"),

;

/**
* 下标
*/
private final int index;

/**
* 默认状态
*/
private final boolean defaultStatus;

/**
* 描述
*/
private final String description;

@Override
public String toString() {
return String.format("SwitchDef(name=%s,description=%s)", name(), description);
}
}

接着定义开关接口Switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface Switch {

/**
* 启用
*
* @param switchDef switchDef
*/
void turnOn(SwitchDef switchDef);

/**
* 关闭
*
* @param switchDef switchDef
*/
void turnOff(SwitchDef switchDef);

/**
* 判断状态
*
* @param switchDef switchDef
* @return boolean
*/
boolean status(SwitchDef switchDef);
}

最后编写开关实现BitSetSwitch和客户端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public enum BitSetSwitch implements Switch {

/**
* 单例
*/
X;

BitSetSwitch() {
init();
}

private final BitSet switches = new BitSet();

@Override
public void turnOn(SwitchDef switchDef) {
switches.set(switchDef.getIndex(), SwitchConst.ON);
}

@Override
public void turnOff(SwitchDef switchDef) {
switches.clear(switchDef.getIndex());
}

@Override
public boolean status(SwitchDef switchDef) {
return switches.get(switchDef.getIndex());
}

private void init() {
Stream.of(SwitchDef.values()).forEach(item -> switches.set(item.getIndex(), item.isDefaultStatus()));
}

public static void main(String[] args) {
Switch s = BitSetSwitch.X;
s.turnOn(SwitchDef.ENABLE_HTTPS);
s.turnOff(SwitchDef.ENABLE_ASYNC);
System.out.printf("开关[%s],状态:%s%n", SwitchDef.ENABLE_HTTPS, s.status(SwitchDef.ENABLE_HTTPS));
System.out.printf("开关[%s],状态:%s%n", SwitchDef.ENABLE_ASYNC, s.status(SwitchDef.ENABLE_ASYNC));
}
}

执行该main方法后控制台输出如下:

1
2
开关[SwitchDef(name=ENABLE_HTTPS,description=启用HTTPS)],状态:true
开关[SwitchDef(name=ENABLE_ASYNC,description=启用异步)],状态:false

仿真场景(伪代码)如下:

1
2
3
4
5
6
7
8
9
10
11
12
Switch s = BitSetSwitch.X;
String uri = "www.throwx.cn";
String schema = "http";
if (s.turnOn(SwitchDef.ENABLE_HTTPS)){
schema = "https";
}
HttpClint ch = new DefaultHttpClient();
if (s.turnOn(SwitchDef.ENABLE_ASYNC)){
ch = new AsyncHttpClient();
}
Result r = ch.executeRequest(schema + uri);
......

小结

在阅读一些主流框架源码的时候,可以借鉴一些设计合理的方案应用到自身的日常开发中。

参考资料: