Я недавно наткнулся на этот класс и был удивлен тем, как добытчики и
сеттеры были реализованы.
Я не сталкивался с этим раньше и приветствовал бы некоторые вторые мнения.
Как вы думаете, это хорошая парадигма?
Это плохо?
Это зло?
Заголовок:
class Tool
{
public:
Tool();
virtual ~Tool();
bool setName(const std::string &name);
bool getName(std::string &name) const;
void clearName();
private:
std::string m_name;
bool m_nameSet;
};
файл cpp:
#include "Tool.h"
Tool::Tool()
: m_name("")
, m_nameSet(false)
{
}Tool::~Tool()
{
}bool Tool::setName(const std::string &name)
{
m_name = name;
m_nameSet = true;
return (m_nameSet);
}
bool Tool::getName(std::string &name) const
{
bool success = false;
if (m_nameSet)
{
name = m_name;
success = true;
}
return (success);
}
Способ, выбранный для получения, не популярен, программисты предпочитают return
данные из геттера
std::string getName() const;
Почему элемент, который был установлен ранее или имеет исходные данные, должен быть перепроверен на получателе? Если вы хотите проверить данные, проверьте их на установщике.
Однако, если вы настаиваете на возвращении значения как «задано имя ранее», вы можете написать третий метод с помощью bool isNameSet() const;
Это очень похоже на C, где обычно возвращаются коды состояния, чтобы увидеть, происходит ли сбой функции или нет.
Тогда также есть лучшие методы, чтобы проверить, установлено имя или нет. Можно было бы использовать Boost :: Option для меня, это лучший способ объявить намерение, что имя не может быть установлен в любое время.
Однако мне хотелось бы знать, не лучше ли убедиться, что имя задано всегда, имея только один конструктор, который принимает std :: string в качестве параметра.
class Tool
{
public:
//Constructor already does the right thing
Tool() = default;
virtual ~Tool();
//Use void or return the modified class, akin to operators
void setName(const std::string& name)
{
m_name = name;
}
//Alternatively
Tool& setName(const std::string &name)
{
m_name = name;
return *this;
}
//Return const reference to the value if possible, avoids copying if not needed
//This will fail at run time if name is not set
//Maybe throw an exception if that is preferred
const std::string& getName() const
{
return *m_name;
//Or
if(m_name) return *m_name;
else throw some_exception;
}
//Or return the optional, then calling code can check if name where set or not
const boost::optional<std::string>& getName() const
{
return m_name;
}void clearName()
{
m_name = boost::optional<std::string>();
}
private:
boost::optional<std::string> m_name;
};
Я бы не назвал это парадигма. Похоже, это решение для архитектуры, где поле может находиться в неопределенном состоянии (почему бы и нет? Иногда это нормальное требование). Хотя мне не очень нравится это решение, потому что getter должен возвращать значение (симметрично, setter должен устанавливать его), а для соглашения обычно требуются специальные прототипы:
Type GetValue();
SetValue (const Type & newValue);
or
SetValue (Type & newValue);
or
SetValue (Type newValue);
Вы должны выбрать один из трех сеттеров в зависимости от ситуации, обычно подходит первый или второй.
Если поле может быть в неопределенном состоянии, я бы выбрал другой подход, как М. М. предлагает в своем ответе, я позволю себе привести пример:
class C
{
private:
int field;
bool fieldSet;
public:
C()
{
field = 0;
fieldSet = false;
}
bool IsFieldSet()
{
return fieldSet;
}
int GetField()
{
if (!fieldSet)
throw std::exception("Attempt to use unset field!");
return field;
}
void SetField(const int newValue)
{
field = newValue;
fieldSet = true;
}
};
Обратите внимание, что я бы не назвал этот способ реализации геттеров злой. Это может быть просто неудобно в использовании.