Страница 55 из 93 49. Именованные константы для булевых величин редко необходимы.
Выбор неверного имени может добавить значительную ненужную сложность в вашу программу. Рассмотрим следующую простейшую функцию, которая подсчитывает количество слов в строке: int nwords(const char *str) { typedef enum { IN_WORD, BETWEEN_WORDS } wstate; int word_count = 0; wstate state = BETWEEN_WORDS; for (; *str ; ++str ) { if ( isspace(*str) ) state = BETWEEN_WORDS;else if ( state != IN_WORD ) { ++word_count; state = IN_WORD; }} return word_count; } Неправильно выбранное имя state заставило нас ввести два ненужных идентификатора: IN_WORD и BETWEEN_WORDS. Теперь взгляните на этот вариант: int nwords2(const char *str) { int word_count = 0; int in_word = 0; for (; *str ; ++str ) { if ( isspace(*str) ) in_word = 0;else if ( !in_word ) { ++word_count; in_word = 1; }} return word_count; } Переименование нечетко названной переменной state во что-нибудь, что действительно описывает назначение переменной, позволило мне исключить булевые именованные константы IN_WORDS и BETWEEN_WORDS. Получившаяся подпрограмма меньше и легче читается. Вот другой пример. Следующая программа: enum child_type { I_AM_A_LEFT_CHILD, I_AM_A_RIGHT_CHILD }; struct tnode { child_type position; struct tnode *left, *right;} t; //... t.position = I_AM_LEFT_CHILD; if ( t.position == I_AM_LEFT_CHILD ) //...может быть упрощена подобным образом: struct tnode { unsigned is_left_child ; struct tnode *left, *right;} t; t.is_left_child = 1; if ( t.is_left_child ) //... тем самым исключая два ненужных идентификатора. И вот последний пример: enum { SOME_BEHAVIOR, SOME_OTHER_BEHAVIOR, SOME_THIRD_BEHAVIOR }; f( SOME_BEHAVIOR , x); f( SOME_OTHER_BEHAVIOR, x); f( SOME_THIRD_BEHAVIOR, x); требующий четырех идентификаторов (три именованные константы и имя функции). Лучше, хотя это не всегда возможно, исключить селекторную константу в пользу дополнительных функций: some_behavior(x); some_other_behavior(x); some_third_behavior(x); Обратной стороной этой монеты является вызов функции. Рассмотрим следующий прототип: create_window( int has_border, int is_scrollable, int is_maximized );Я снова выбрал рациональные имена для исключения необходимости в именованных константах. К сожалению, вызов этой функции плохо читаем: create_window( TRUE, FALSE, TRUE );Просто взглянув на такой вызов, я не получу никакого представления о том, как будет выглядеть это окно. Несколько именованных констант проясняют обстоятельства в этом вызове: enum { UNBORDERED =0; BORDERED =1}; // Нужно показать значения enum { UNSCROLLABLE=0; SCROLLABLE =1}; // или create_window() enum { NORMAL_SIZE =0; MAXIMIZED =1}; // не будет работать. //... create_window( BORDERED, UNSCROLLABLE, MAXIMIZED ); но теперь у меня другая проблема. Я не хочу использовать именованные константы внутри самой create_window(). Они здесь только для того, чтобы сделать ее вызов более читаемым, и я не хочу загромождать эту функцию таким кодом, как: if ( has_border == BORDERED ) //... сравнивая его с более простым: if ( has_border ) //... Первый вариант уродлив и многословен. К сожалению, если кто-то изменит значение именованной константы BORDERED, второй оператор if не будет работать. Я обычно соглашаюсь с мнением, что программист, занимающийся сопровождением, не должен менять значения идентификаторов, как я это проделал в предыдущем примере. |