Previous Entry Поделиться Next Entry
необычные геттеры и сеттеры
artem_talipov
В продолжение про сеттеры и геттеры
.

Мою предыдущую запись можно назвать идиалистичной, а мир как мы знаем, такого не любит.
Никто мне конечно же, и слова поперёк не сказал. У! Пусть только попробуют!
А покритиковать я и сам могу. Пусть это даже будет моя собственная запись. Как известно, критика полезна для поиска истины.

В некоторых случаях, сеттер вынужден серьёзно переделывать объект. Утверждение весьма спорное, но давайте посмотрим ближе.

Вот к примеру метод set_copy, или set_clone, который задаёт копию объекта.
Ха-эм. Этот метод полностью перемалывает старый объект, делая из него копию переданного.
Вообще-то, это вполне правомочно. В данном случае явным образом подразумеваеться полная замена всех полей объекта.

Можем для примера взять объект времени. У него есть поля секунды, и часы. Их можно задавать по отдельности, а можно сразу.
Это нормально и приемлимо, просто получаеться мульти сеттер.

Точно так же как мульти геттер, возвращающий значение нескольких полей сразу.
К примеру get_copy.

Есть ещё один вариант, когда сеттер вынужден переделывать объект. Частично я уже коснулся этого вопроса.
Имею в виду string::set_size или как было решено его назвать string::resize.
Этот случай был явный. Чтобы изменить значения одного подразумеваемого поля, нужно было изменять и другие поля. Что может быть очевидно для разработчиков класса string, и неочевидно для пользователя объекта типа string.

Вот ещё, более сложный случай. Всё тот же класс string. get_element возвращает элемент по указанному индексу. Если строка пуста, или индекс задан неверно, то будет брошено исключение.

Да-да, это тоже геттер, или индексер, в прочем его можно назвать и мини геттером. Он введь возвращает не значение поля, а часть значения поля.


elem_type string::get_element( unsigned int index ) const
{

// если строка пуста
if( this->is_empty() )
{
throw not_data();
}

// если индекс неверен
if( index >= this->i_length )
{
throw index_out_of_bounds();
}

return( this->i_value[ index ] );
}


А теперь в противоположность get_element, рассмотрим set_element.
Смысл его заключаеться в том, чтобы задать значение по определённому индексу


void string::set_element( unsigned int index, elem_type ch )
{

// если строка пуста
if( this->is_empty() )
{
throw not_data();
}

// если индекс недопустим
if( index >= this->i_size )
{
throw index_out_of_bounds();
}

// если строка короче
if( index >= this->i_length )
{
this->i_length = index;
}

this->i_value[ index ] = ch;
}


Это правильный сеттер. Он изменяет одно поле... Гэ-ха-эм... И корректирует ещё одно. Вот же! А может, нафиг эту корректировку? Пользователь потом сам задаст нужное значение?
Сложный вопрос.... Ладно, давайте считать, что это смешанный сеттер, изменяющий сразу два поля. Это вполне правомочно, поскольку, метод явным образом принимает сразу два параметра.

Но давайте пойдём дальше. Считаеться, (не знаю кем, наверное это очередная утка), что лишние исключения это плохо.
Ладно, допустим это действительно так. А значит, высшая обязанность разработчика сделать так, чтобы исключений было как можно меньше.
В случае с get_element, это не реально. Все исключения на своём месте.
А вот в случае с set_element, есть варианты.

Если строка, для которой задаёться элемент, вдруг оказалась пуста, то почему бы её не создать?
Упс... Это мега вмешательство в объект. А значит, сеттер, повлияет на весь объект, что не предполагалось! Ведь пользователь хотел добавить элемент!
Но зато, пользователь объекта, вместо исключения, получит строку, с заданным элементом, и без исключений. Хе-хе, что просил, то и получил!

Ладно, а если строка короче? В смысле память для элемена с нужным индексом ещё не зарезервирована? А почему бы не изменить размер?
И снова пользователь получает именно то, что он просил, а весь объект будет перестроен.

Да какой-же это сеттер? Это уже метод какой-то получаеться! Не! Дану его нафиг!
Если пользователь такой придурок, что просит полный бред, это ещё не значит, что нужно разбиваться в лепёшку, но выполнять всякие идиотские пожелания.
А может пользователь ошибся? Может у него выскочил индекс, которого он и не предполагал. А вместо того, чтобы предупредить его о потенциальной ошибке, объект покорно делает то что ему сказано. Это уже называеться замалчивание ошибки.
Лучше сделать сеттер с исключениями, и отдельный метод, который сделает всё что скажут. А не извращаться по пусту.

А из этого можно вывести ещё одно правило.


Геттер или сеттер, могут вызывать только геттеры или сеттеры, но не методы изменяющие объект.
(считай что все методы).



Меня можно упрекнуть в том, что я противоречу сам себе, ткнув в метод is_empty.

Я ещё в предыдущей записи хотел о них поговорить, но руки не дошли.

В какой-то мере такие методы как is_empty, is_good, is_filled, is_bad, is_eof, являются тоже геттерами. В прочем это не так. Это просто индекаторы, которые показывают (проверяют) состояние определённых полей.

И ведь я в правиле написал "методы изменяющие объект". Ведь и геттер и сеттер, это тоже методы... Короче, это правило требует не технических а понятийных навыков.

Для геттеров это можно проконтролировать на уроввне компиляции. Нужно лишь метод создавать с модификатором const.
Из такого метода, есть права вызывать только другие методы, с модификатором const.
И метод с модификатором const, не имеет прав изменять значение полей.
Эти права конечно обходяться, стоит лишь сделать this неконстантным. Ну, да речь не об этом.

а вот сеттеры, тут конечно сложнее, и ответственность ложиться на совесть разработчиков.


?

Log in