库文件最好少使用硬编码值!

乔梁 | 2021-04-14

你可能会遇到这样一种情况:你的某个函数总是使用相同的一个值,那么你此时可能会定义一个常量。这是一种好的做法,因为它避免了“魔数”,并改善了代码可读性。但是,需要注意的是:这种硬编码的值会让可用性和未来可能的重构动作变得非常困难。

看看下面这个函数,它依赖于一些硬编码:

// Declared in the module.
constexpr int kThumbnailSizes[] = {480, 576, 720};

// Returns thumbnails of various sizes for the given image.
std::vector<Image> GetThumbnails(const Image& image) {
  std::vector<Image> thumbnails;
  for (const int size : kThumbnailSizes) {
    thumbnails.push_back(ResizeImage(image, size));
  }
  return thumbnails;
}

使用硬编码值( hardcoded value )让你的代码:

  • 减少了可预测性: The caller might not expect the function to be relying on hardcoded values outside its parameters; a user of the function shouldn’t need to read the function’s code to know that. Also, it is difficult to predict the product/resource/performance implications of changing these hardcoded values.

  • 降低了重用性: 调用者无法使用不同的值来调用这个函数,只能使用这个硬编码的值。如果调用者不需要上面列出的所有这些 sizes ,或者需要一个不同的 size,这个函数就不得不强行做个分叉,或者通过重构,从而避免上述复杂性。

当设计一个库( a library )时,建议传入所需的值,比如通过一个函数调用或者一个构造器。上面的代码可以修改成下面的形式:

std::vector<Image> GetThumbnails(const Image& image, absl::Span<const int> sizes) {
  std::vector<Image> thumbnails;
  for (const int size : sizes) {
    thumbnails.push_back(ResizeImage(image, size));
  }
  return thumbnails;
}

针对某个参数,如果大多数的调用者都传入相同的值,就让你的代码变得具有可配置性,以便每个调用者不必重复一次。例如,你可以定义一个 public 的常量,包含某个常用的值,或者在某些支持设定参数默认值的语言中,也可以直接指定默认值(例如 C++ ,Python 等)。

// Declared in the public header.
inline constexpr int kDefaultThumbnailSizes[] = {480, 576, 720};

// Default argument allows the function to be used without specifying a size.
std::vector<Image> GetThumbnails(const Image& image,
                                 absl::Span<const int> sizes = kDefaultThumbnailSizes);

需要注意的一点是:对任何代码来说,验证用户的输入是非常重要的。但是,为了减少示例代码,并且更聚焦于本文想讨论的话题,所以忽略了这些必要的验证代码。

原文作者:Adel Saoud

原文链接:Avoid Hardcoding Values for Better Libraries

发表时间:August 19, 2020