Я пишу множество функций, которые начинаются со многих предварительных условий, а затем я должен выяснить, как обрабатывать все недопустимые входные данные и писать для них тесты.
Обратите внимание, что кодовая база, в которой я работаю, не позволяет генерировать исключения, если это становится актуальным в этом вопросе.
Мне интересно, существует ли какой-либо шаблон проектирования C ++, в котором вместо предварительных условий входные аргументы передаются через классы-оболочки, которые гарантируют инварианты. Например, предположим, что я хочу, чтобы функция возвращала максимальное значение в векторе целых чисел. Обычно я бы сделал что-то вроде этого:
// Return value indicates failure.
int MaxValue(const std::vector<int>& vec, int* max_value) {
if (vec.empty()) {
return EXIT_FAILURE;
}
*max_value = vec[0];
for (int element : vec) {
if (element > *max_value) {
*max_value = element;
}
}
return EXIT_SUCCESS;
}
Но мне интересно, если есть шаблон проектирования, чтобы сделать что-то вроде этого:
template <class T>
class NonEmptyVectorWrapper {
public:
static std::unique_ptr<NonEmptyVectorWrapper>
Create(const std::vector<T>& non_empty_vector) {
if (non_empty_vector.empty()) {
return std::unique_ptr<NonEmptyVectorWrapper>(nullptr);
}
return std::unique_ptr<NonEmptyVectorWrapper>(
new NonEmptyVectorWrapper(non_empty_vector));
}
const std::vector<T>& vector() const {
return non_empty_vector_;
}
private:
// Could implement move constructor/factory for efficiency.
NonEmptyVectorWrapper(const std::vector<T>& non_empty_vector)
: non_empty_vector_(non_empty_vector) {}
const std::vector<T> non_empty_vector_;
};
int MaxValue(const NonEmptyVectorWrapper<int>& vec_wrapper) {
const std::vector<int>& non_empty_vec = vec_wrapper.vector();
int max_value = non_empty_vec[0];
for (int element : non_empty_vec) {
if (element > max_value) {
max_value = element;
}
}
return max_value;
}
Основным преимуществом здесь является то, что вы избегаете ненужной обработки ошибок в функции. Более сложный пример, где это может быть полезно:
// Finds the value in maybe_empty_vec which is closest to integer n.
// Return value indicates failure.
int GetValueClosestToInt(
const std::vector<int>& maybe_empty_vec,
int n,
int* closest_val);
std::vector<int> vector = GetRandomNonEmptyVector();
for (int i = 0; i < 10000; i++) {
int closest_val;
int success = GetValueClosestToInt(vector, i, &closest_val);
if (success) {
std::cout << closest_val;
} else {
// This never happens but we should handle it.
}
}
который расточительно проверяет, что вектор не пуст каждый раз, и проверяет наличие ошибок, по сравнению с
// Returns the value in the wrapped vector closest to n.
int GetValueClosestToInt(
const NonEmptyVectorWrapper& non_empty_vector_wrapper,
int n);
std::unique_ptr<NonEmptyVectorWrapper> non_empty_vector_wrapper =
NonEmptyVectorWrapper::Create(GetRandomNonEmptyVector());
for (int i = 0; i < 10000; i++) {
std::cout << GetValueClosestToInt(*non_empty_vector_wrapper, i);
}
который не может потерпеть неудачу и избавляется от ненужной проверки ввода.
Является ли этот шаблон дизайна хорошей идеей, есть ли лучший способ сделать это, и есть ли название для него?
Задача ещё не решена.
Других решений пока нет …