С++ Get/Set accessors - как мне избежать ввода повторяющегося кода?
Я пишу довольно большую библиотеку, и я нахожу, что пишу почти идентичные аксессоры все время. У меня уже есть несколько десятков аксессуаров, таких как ниже.
Вопрос. Как я могу объявить/реализовать средства доступа, чтобы сохранить ввод всего этого повторяющегося кода? (Нет #defines, пожалуйста, я ищу С++-конструкции.)
Обновление. Да, мне нужны функции доступа, потому что мне нужно использовать указатели для этих аксессуаров для чего-то, называемого дескрипторами свойств, которые позволяют значительно экономить на моем графическом интерфейсе (не-библиотеке).
.h файл
private:
bool _visible;
public:
bool GetVisible() const { return _visible; }
void SetVisible (bool value);
// Repeat for Get/SetFlashing, Get/SetColor, Get/SetLineWidth, etc.
.cpp file
void Element::SetVisible (bool value)
{
_visible = value;
this->InvalidateSelf(); // Call method in base class
// ...
// A bit more code here, identical in 90% of my setters.
// ...
}
// Repeat for Get/SetFlashing, Get/SetColor, Get/SetLineWidth, etc.
Ответы
Ответ 1
В то время как я считаю, что гонки на легкость на орбите очень полезны, есть также несколько способов, которые могут быть использованы для реализации "повторяющегося кода", который может быть применен, если у нас действительно есть класс, который имеет "много вещей" которые схожи, что нужно контролировать индивидуально, так что продолжайте это, скажем, у нас есть несколько способов:
void Element::Show()
{
visible = true;
Invalidate();
// More code goes here.
}
void Element::Hide()
{
visible = false;
Invalidate();
// More code goes here.
}
Теперь, на мой взгляд, это нарушает принцип DRY (Do not Repeat Yourself), поэтому мы должны, вероятно, сделать что-то вроде этого:
void Element::UpdateProperty(bool &property, bool newValue)
{
property = value;
Invalidate();
// More code goes here.
}
Теперь мы можем реализовать Show
и Hide
, Flash
, Unflash
, Shaded
и т.д., избегая повторения внутри каждой функции.
void Element::Show()
{
UpdateProperty(visible, true);
}
Если тип не всегда bool, например. существует position
, мы можем сделать:
template<typename T>void Element::UpdateProperty(T &property, T newValue)
{
property = value;
Invalidate();
// More code goes here.
}
и MoveTo
становится:
void Element::MoveTo(Point p)
{
UpdateProperty(position, p);
}
Изменить на основе ранее нераскрытой информации, добавленной к вопросу:
Очевидно, что вышеупомянутая техника может быть одинаково применима к любой форме функции, которая выполняет такую работу:
void Element::SetVisible(bool value)
{
UpdateProperty(visible, value);
}
будет работать так же хорошо, как и для Show
, описанного выше. Это не означает, что вы можете уклониться от объявления функций, но это уменьшает потребность в коде внутри функции.
Ответ 2
Я нахожу, что пишу почти идентичные аксессоры все время. У меня уже есть несколько десятков аксессуаров, таких как ниже.
Это уверенный дизайн запаха, что вы пишете аксессоры "ради него". Вам они действительно нужны? Вам действительно нужна низкоуровневая публичная операция "получить" и "установить" для каждого из них? Это маловероятно.
В конце концов, если все, что вы делаете, это запись геттера и сеттера для каждого частного элемента данных, и каждый из них имеет одну и ту же логику, вы также можете просто сделать участников данных общедоступными.
Скорее, ваш класс должен иметь смысловые и семантические операции, которые в ходе выполнения своих обязанностей могут или не могут использоваться частными членами данных. Вы обнаружите, что каждая из этих значимых операций сильно отличается от остальных, и поэтому ваша проблема с повторяющимся кодом побеждена.
As n.m. сказал:
Легко: избегайте доступа к аксессуарам. Программируйте свои классы, чтобы что-то делать, а не что-то.
Даже для тех операций, для которых нет ничего больше, например контроля видимости, вы должны иметь bool isVisible() const
и void show()
, а void hide()
. Вы обнаружите, что когда вы начнете кодирование таким образом, это будет способствовать отходу от шаблона "ради него" геттеров и сеттеров.
Ответ 3
Я согласен с Легкостью. Вы должны разработать классы для этой задачи, и если вам нужно столько геттеров и сеттеров, вы можете сделать что-то неправильно.
Тем не менее, большинство хороших IDE позволяют создавать простые геттеры и сеттеры, а некоторые даже могут позволить вам их настроить. Вы можете сохранить повторяющийся код в качестве шаблона и при необходимости выбрать фрагмент кода.
Вы также можете использовать настраиваемый редактор, такой как emacs и Vim (с помощью Ultisnips), и создать некоторые пользовательские функции помощи, чтобы упростить вашу работу. Задача созрела для автоматизации.
Ответ 4
Единственный раз, когда вы должны когда-либо писать набор функций get/set на любом языке, - это если он делает что-то другое, кроме простого чтения или записи простой переменной; не беспокойтесь об обертывании доступа к данным, если все, что вы делаете, затрудняет чтение людей. Если это то, что вы делаете, прекратите делать что-нибудь.
Если вам когда-либо нужен набор функций get/set, не вызывайте их get и set - используйте назначение и тип casting (и делайте это с умом). Таким образом, вы можете сделать свой код более читаемым, а не меньше.
Это очень неэлегантно:
class get_set {
int value;
public:
int get() { return value; }
void set(int v) { value = v; }
};
Это немного лучше
class get_set_2 {
value_type value;
bool needs_updating;
public:
operator value_type const & () {
if (needs_updating) update(); // details to be found elsewhere
return value;
}
get_set_2& operator = (value_type t) {
update(t); // details to be found elsewhere
return *this;
}
};
Если вы не выполняете второй шаблон, ничего не делайте.