Рассмотрим следующий код (который является упрощенной версией исходной):Должен ли я создать класс вместо повторяющихся операторов get/set?
#include <string>
namespace my
{
class CType;
}
class my::CType
{
private:
std::string simple_name;
public:
explicit CType(std::string simple_name)
: simple_name{std::move(RequireIdentifier(simple_name))} // Duplicate Code
{
}
std::string get_simple_name() const
{
return simple_name;
}
void set_simple_name(std::string value)
{
simple_name = std::move(RequireIdentifier(value)); // Duplicate Code
}
};
RequireIdentifier
функция возвращает ссылку на value
где value
не пусто в противном случае он будет бросать std::invalid_argument
исключение.
У меня слишком много классов, которые аналогичны определению CType
класса, все из них имеют закрытую переменную типа std::string
, а затем они проверяют его значение не пусто со следующим выражением:
std::move(RequireIdentifier(value))
Вот моя проблема, она повторяется во всех этих классах! предположим, что у меня есть 10 классов, подобных CType
, тогда должно быть 20 = 2 * 10 выражений, подобных приведенному выше случаю! я не хочу повторять аналогичный код во всех 20 местах кода! он ошибается.
Я думаю, что я должен определить новый тип класса:
class my::Identifier
{
private:
std::string name;
public:
explicit Identifier(std::string name)
: name{std::move(RequireIdentifier(name))} // Duplicate Code
{
}
std::string get() const
{
return name;
}
void set(std::string value)
{
name = std::move(RequireIdentifier(value)); // Duplicate Code
}
};
А затем изменить определение класса CType
как следующий код:
class my::CType
{
private:
Identifier simple_name;
public:
explicit CType(Identifier simple_name)
: simple_name{std::move(simple_name)}
{
}
Identifier get_simple_name() const
{
return simple_name;
}
void set_simple_name(Identifier value)
{
simple_name = std::move(value);
}
};
BTW, я не могу избежать повторяющийся раздел std::move
, и это не проблема.
Но если я не перегружать =
оператор Identifier
класса, и если я не объявить неявное преобразование в std::string
для Identifier
класса, то Identifier
класс не будет работать как string
класса. Тогда это не плохой выбор дизайна? Каким образом можно избежать повторного кода в таком случае (AFAIK, мы не должны подклассы из любого контейнера STL)?
EDIT: функция RequireIdentifier
определяется как следующий код:
inline std::string& my::RequestIdentifier(std::string& value)
{
for(char c : value)
{
if(!isalnum(c) && c != '_')
{
throw std::invalid_argument{""};
}
}
return value;
}
inline std::string& my::RequireIdentifier(std::string& value) // HERE IS IT
{
if(value.empty())
{
throw std::invalid_argument{""};
}
else
{
return my::RequestIdentifier(value);
}
}
Заключение
Как вы можете видеть, если я поставил «извлечение» из основной строки в моем CType
классе (как указано в принятом ответе Tony D), и если я хочу работать с типом string
, тогда я должен вызвать функции и set
функций Identifier
класс во всех моих 10 классах повторяется, так как я назвал функцию RequireIdentifier
. Поэтому создание класса только для устранения избыточности одного вызова функции, технически не является хорошим подходом.
С другой стороны, и независимо от кода избыточности, я должен объявить Identifer
класс, если я действительно думаю, что это новый тип, что string
тип не может представлять это хорошо, и я могу объявить неявное определенный пользователем конструктор для того, чтобы сделать его совместимым с типом string
, и, наконец, цель класса Identifier
не должна быть аксессуаром get
и set
для типа string
, его назначение должно быть нового типа.
Какова функция причина 'RequireIdentifier'? Кажется немного странным, что он возвращает то, что вы пытаетесь переместить. Что произойдет, если вы дважды перейдете из одной и той же lvalue? – juanchopanza
@juanchopanza: Он определен, чтобы избежать повторения проверки, если строка пуста. Я включил его определение. –
Класс с чистыми функциями set/get над инженерами, просто структуры должно быть достаточно? или просто строка должна быть достаточно? – billz