Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ | страница 69



При разработке интерфейсов, простых для правильного применения и трудных – для неправильного, вы должны предвидеть, какие ошибки может допустить пользователь. Например, предположим, что вы разрабатываете конструктор класса, представляющего дату:


>class Date {

>public:

>Date(int month, int day, int year);

>...

>};


На первый взгляд, этот интерфейс может показаться разумным (во всяком случае, в США), но есть, по крайней мере, две ошибки, которые легко может допустить пользователь. Во-первых, он может передать параметры в неправильном порядке:


>Date(30, 3, 1995); // должно быть “3, 30”, а не “30, 3”


Во-вторых, номер месяца или дня может быть указан неверно:


>Date(2, 20, 1995); // Должно быть “3, 30”, а не “2, 20”


(Последний пример может показаться надуманным, но вспомните, что на клавиатуре «2» находится рядом с «3». Такие опечатки случаются сплошь и рядом.)

Многих ошибок можно избежать за счет введения новых типов. Система контроля типов – ваш первый союзник в деле предотвращения компилируемости нежелательного кода. В данном случае мы можем ввести простые типы-обертки, чтобы различать дни, месяцы и годы, затем использовать их в конструкторе Date:


>struct Day { struct Month { struct Year {

>explicit Day(int d) explicit Month(int m) explicit Year(int y)

>: val(d) {} : val(m) {} : val(y) {}

>int val; int val; int val;

>}; }; };

>class Date {

>public:

>Date(const Month& m, const Day& d, const Year& y(;

>...

>};

>Date d(30, 3, 1995); // ошибка! неправильные типы

>Date d(Day(30), Month(3), Year(1995); // ошибка! неправильные типы

>Date d(Month(3), Day(30), Year(1995)); // порядок, типы корректны


Еще лучше сделать Day, Month и Year полноценными классами, инкапсулирующими свои данные (см. правило 22). Но даже применение простых структур наглядно демонстрирует, что разумное использование новых типов способно эффективно предотвратить ошибки при использовании интерфейсов.

После того как определены правильные типы, иногда имеет смысл ограничить множество принимаемых ими значений. Например, есть только 12 допустимых значений месяцев, что и должен отразить тип Month. Один из способов сделать это – применить перечисление (enum) для представления месяца. Но перечисления не так безопасны по отношению к типам, как хотелось бы. Например, перечисления могут быть использованы как значения типа int (см. правило 2). Более безопасное решение – определить набор допустимых месяцев:


>class Month {

>public:

>static Month Jan() {return Month(1);} // функции возвращают все