API
设计原则:不引诱人除以零
一个团队正带来一个API
以供审查,其中一个部分涉及以三个数字表达一组支持的值
:
1,允许的最小值
.
2,增量
.
3,允许的最大值
.
支持的值
是增量
与最小值
相加的最小值
整数倍,最多(包括最大值
).
如,如果最小值
为5
,增量为10
,最大值
为30
,则有效值
为
cpp
5|Minimum
15|Minimum + 1 * Increment
25|Minimum + 2 * Increment
30|Maximum
该团队还说明,如果增量为零
,则只支持最小值
和最大值
.
我说明,该设计
会诱使用户除零
.
这是你可能编写的函数:
cpp
int closestSupportedValue(int desired)
{
int nearest = minimum +
((desired - minimum + increment/2) / increment) * increment;
return std::clamp(nearest, minimum, maximum);
}
首先找到超出最接近
要求值的最小值
的增量倍数.,可通过适当更改及增量/2
来圆整.然后在允许的范围内限制该值.
除非增量为零
,否则这很有效.如,你可能在所有测试
中都不会遇见零,因为他们测试代码的所有硬件
都支持广泛的可能值
.
他们从未意识到必须在仅支持1或2个
可能值的硬件上测试.
我建议团队从API
中删除零.如果仅支持两个值
,则按等于最大值-最小值
设置增量.如果仅支持一个值
,则按1
设置增量.
:电网团队
落入了该陷阱.PowerGridForecast
类表示一系列从StartTime
开始,每个预测表示BlockDuration
描述的时间长度的PowerGridData
对象.
即,向量的第n个符号
表示从StartTime+n*BlockDuration
开始,并持续到BlockDuration
的预测.
要编写一个查找描述特定时间点
的块的函数,可如下:
cpp
PowerGridData FindForecastForDateTime(
PowerGridForecast forecast, DateTime time)
{
var elapsed = time - forecast.StartTime;
var index = elapsed / forecast.BlockDuration;
if (index < 0 || index >= forecast.Forecast.Count) {
return null;
//该索引没有预测
}
return forecast.Forecast[index];
}
确定自第一次
预测开始以来经过的时间,确定它属于哪个块
索引,(如果有)并返回该索引
处的符号.
该团队决定,如果没有预测,则Forecast
属性将返回一个空向量
(良好),而BlockDuration
将为零(坏).
该团队认为,如果没有预测,区块大小
就不重要,因为它描述的是不存在的东西,所以谁在乎它是否为零,但在上面看到,零区块持续时间
表明索引的计算
执行除零计算
.
因为区块大小
不重要,他们应该选择一个不太可能
引起问题的值.选择一个典型有效块大小,如一小时的非零块大小
.
实际上,可按大于最大值-最小值
的任何正值
设置增量,尽管不想选择太大的值
,因为这有整数溢出
的风险.