Nguyên tắc thiết kế API: Đừng dụ dỗ người dùng chia cho số không

Vấn đề với giá trị gia tăng bằng không trong thiết kế API

Một nhóm phát triển đang trình bày một API để xem xét, và một phần của API liên quan đến việc biểu diễn một tập hợp giá trị được hỗ trợ dưới dạng ba con số:

  • Giá trị tối thiểu cho phép.
  • Bước gia tăng.
  • Giá trị tối đa cho phép.

Các giá trị được hỗ trợ là giá trị tối thiểu, các bội số nguyên của bước gia tăng cộng thêm vào giá trị tối thiểu, cho đến và bao gồm giá trị tối đa.

Ví dụ: nếu giá trị tối thiểu là 5, bước gia tăng là 10 và giá trị tối đa là 30, thì các giá trị hợp lệ là:

  • 5 (Giá trị tối thiểu)
  • 15 (Giá trị tối thiểu + 1 × Bước gia tăng)
  • 25 (Giá trị tối thiểu + 2 × Bước gia tăng)
  • 30 (Giá trị tối đa)

Nhóm cũng lưu ý rằng nếu bước gia tăng bằng không, thì các giá trị được hỗ trợ chỉ là giá trị tối thiểu và tối đa.

Tôi chỉ ra rằng thiết kế này dụ dỗ người dùng chia cho số không.

Hàm tính giá trị được hỗ trợ gần nhất

Đây là một hàm bạn có thể viết:


int closestSupportedValue(int desired)
{
int nearest = minimum +
((desired - minimum + increment/2) / increment) * increment;
return std::clamp(nearest, minimum, maximum);
}

Chúng ta đầu tiên tìm bội số của bước gia tăng ngoài giá trị tối thiểu gần nhất với giá trị mong muốn. (Bạn có thể điều chỉnh cách làm tròn bằng cách thay đổi +increment/2 phù hợp.) Sau đó chúng ta giới hạn giá trị đó trong phạm vi cho phép.

Điều này hoạt động tốt trừ khi bước gia tăng bằng không. Và có thể nhà phát triển không bao giờ gặp phải giá trị không trong tất cả các bài kiểm tra của họ, ví dụ, vì tất cả phần cứng họ kiểm tra mã của họ hỗ trợ một phạm vi giá trị có thể rộng. Họ không bao giờ nhận ra rằng họ phải kiểm tra trên phần cứng chỉ hỗ tr trợ 1 hoặc 2 giá trị có thể.

Tôi đề nghị nhóm loại bỏ số không khỏi API. Nếu chỉ có hai giá trị được hỗ trợ, thì đặt bước gia tăng bằng maximum - minimum. Nếu chỉ có một giá trị được hỗ trợ, thì đặt bước gia tăng thành 1.

Câu chuyện thêm: Đội ngũ lưới điện đã rơi vào bẫy này

Lớp PowerGridForecast biểu diễn một chuỗi các đối tượng PowerGridData, bắt đầu từ StartTime, nơi mỗi dự đoán đại diện cho một khoảng thời gian được mô tả bởi BlockDuration. Nói cách khác, phần tử thứ n của vector đại diện cho một dự đoán bắt đầu tại StartTime + n × BlockDuration và kéo dài trong BlockDuration.

Để viết một hàm tìm khối mô tả một thời điểm cụ thể, bạn có thể đưa ra một cái gì đó như thế này:


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; // không có dự đoán cho chỉ mục này
}
return forecast.Forecast[index];
}

Chúng ta xác định thời gian đã trôi qua kể từ khi bắt đầu dự đoán đầu tiên, xác định chỉ mục khối mà nó thuộc về và trả về phần tử tại chỉ mục đó, nếu nó tồn tại.

Nhóm quyết định rằng nếu không có dự đoán nào, thì thuộc tính Forecast sẽ trả về một vector rỗng (tốt), và BlockDuration sẽ bằng không (xấu).

Nhóm cho rằng nếu không có dự đoán, kích thước khối là không liên quan vì nó đang mô tả một thứ không tồn tại, vậy ai quan tâm nếu nó bằng không? Nhưng chúng ta đã thấy ở trên rằng thời lượng khối bằng không có nghĩa là phép tính chỉ mục thực hiện phép chia cho số không.

Vì kích thước khối là không liên quan, họ nên chọn một giá trị khó có thể gây ra vấn đề. Chọn một kích thước khối khác không điển hình của một kích thước khối hợp lệ, ví dụ như một giờ. Trong thực tế, hầu hết các nhà phát triển đang kiểm tra trên các máy có kết nối internet tốt và nằm ở các khu vực có dữ liệu dự báo lưới điện tốt. Họ không có máy kiểm tra ở một quốc gia xa xôi.

Ghi chú:

1. Hoặc thực sự, bạn có thể đặt bước gia tăng thành bất kỳ giá trị dương nào lớn hơn maximum - minimum, mặc dù bạn không muốn chọn một giá trị quá lớn, vì điều đó có nguy cơ tràn số nguyên.

2. Và họ khó có thể có ngân sách để đưa một nhà phát triển đến một quốc gia xa xôi chỉ để đảm bảo trường hợp kiểm tra này vượt qua. “Sếp, điều quan trọng là chúng ta có một nhà phát triển ở Tahiti, để đảm bảo chúng ta có phạm vi kiểm tra phù hợp.”

Chỉ mục