Подтвердить что ты не робот

С++ 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.
4b9b3361

Ответ 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;
    }
};

Если вы не выполняете второй шаблон, ничего не делайте.