LINUX.ORG.RU

Get*/Set* отдельными методами или одним, какой вариант более православный?

 


1

5

Приветствую, пишу портянку и всплыл вопрос, какой из двух вариантов на ваш взгляд православней и почему?

class A {
public:
    int  getVal() { return _val; }
    void setVal(int val) { _val = val; }
private:
    int _val;
}

или получение и установку переменной-члена класса объединить в одном методе:

class A {
public:
    int val(int val=-1) { if(val > -1) return _val; else _val = val; }
private:
   int _val;

}
имеет ли второй вариант право на жизнь, или он только для хипстеров волосатых под накуркой? )

★★★★★

Уж лучше перегрузка, чем второй вариант.

DELIRIUM ★★★★★ ()

Второй для дегенератов, а не для хипсторов.

ritsufag ★★★★★ ()

Второй вариант - быдлокод.

Достаточно первого без get, ака val().

PS:

int  value() const { return _val; }
void setValue(int val) { m_val = val; }

RazrFalcon ★★★★★ ()

не, ну нафиг ваши кресты. В моем болоте с plain c живется куда как проще.

demidrol ★★★★★ ()

Какой упоротый человек придумал второй вариант? Перегрузка методов на что придумана?

int val() { return _val; }
void val(int val) { _val = val; }

Автоматом получаем поддержку _val == -1 и какой-никакой контроль ошибок использования val() со стороны компилятора.

Но вообще мне больше нравится вариант Get*/Set* отдельными методами или одним, какой вариант более православный? (комментарий)

KivApple ★★★★★ ()
Последнее исправление: KivApple (всего исправлений: 2)

И в первом, и во втором случаях «геттер» неконстантный, как их использовать из константного метода?

Как установить отрицательное значение?

Условие на каждый вызов - сброс конвейера на каждое ошибочное предсказание.

andreyu ★★★★★ ()

в Qt применяются раздельные методы value()/setValue(const V& v);

вроде бы никто не жалуется.

один метод выгядит грешно, т.к. тебе придется резервировать некоторое значение под «не устанавливать». в будущем это может привести к внезапным обосракам, да и посоны не поймут.

i36_zubov ()
Ответ на: комментарий от andreyu

Как установить отрицательное значение?

Нужно расширить костыль использованием std::option =)

RazrFalcon ★★★★★ ()
Ответ на: комментарий от andreyu

Всё, меня отпустило, мысли дурные ушли и я прозрел. Действительно, во втором варианте часто может возникать неоднозначность, благодарю за исцеление ))

xterro ★★★★★ ()

Второй вариант не только бредовый по уже перечисленным причинам, но ещё и менее эффективный (если только компилятор не заинлайнит).

Deleted ()

Не используй инкапсуляцию, и не надо геты и сеты писать.

impowski ()
Ответ на: комментарий от impowski

геты и сеты нужны когда:

  • у тебя при изменении переменной надо что-то делать, например:
    • проверку значений, которые ты присваиваешь
    • логирование
    • изменение каких-то других переменных в момент присваивания
    • извращенная отладка (вешаешь на метод get нужные тебе значения и смотришь на результат, вне зависимости от того, что на самом деле в переменной)
  • для создания API, когда логику внутри программы/библиотеки тебе, возможно, придётся поменять 100500 раз.
peregrine ★★★★★ ()

Тут лет 5-6 назад был эпичный тред про то, что геттеры и сеттеры не нужны, даешь прямой доступ! По мотивам статьи тов. Голуба

yoghurt ★★★★★ ()

для хипстеров волосатых под накуркой

This.

WRG ★★★★ ()

Делай сразу свойства, чего уж.

#include <iostream>


template <typename T>
class Property {
private:
    T value;
    Property & operator=(const Property &) = delete;
    Property(const Property &) = delete;
    Property() = delete;
public:
    explicit Property(const T &value) {
        this->value = value;
    }

    const T& operator=(const T &value) {
        this->value = value;
        return value;
    }

    operator T() const {
        return value;
    }
};

template <typename T>
std::ostream& operator<<(std::ostream &os, const Property<T> &prop) {
    os << static_cast<T>(prop);
    return os;
}

template <typename T>
std::istream& operator>>(std::istream &is, Property<T> &prop) {
    T val;
    is >> val;
    prop = val;
    return is;
}


class A {
public:
    Property<int> val {0};
};


int main() {
    A obj;
    std::cin >> obj.val;
    std::cout << obj.val;

    return 0;
}

evilface ★★ ()
Ответ на: комментарий от evilface

Сделать только operator= и operator T() виртуальными, наследоваться, переопределять, использовать.
Зачем — не знаю.

evilface ★★ ()
Ответ на: комментарий от evilface

Разве это не убивает инкапсуляцию? Ну и невозможность менять поведение сеттера огорчает. Уж для очень примитивных случаев этот вариант подходит.

Но на правах наркомании сойдёт.

RazrFalcon ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Ну, если сделать виртуальные .get и .set, которые будут в операторах вызываться (для красоты), то можно внутри класса создавать подкласс этого свойства, переопределять, и будет как в каком-нибудь си-шарпе.

evilface ★★ ()

получение и установку переменной-члена класса объединить в одном методе:

это какраз тот случай опасных для общества девиаций.

Вот скажи какой процесс в голове вызвал у тебя рождение этого варианта и что натолкнуло на мысль что это адекватно?

Даже врапперы свойств выглядят не так упорото.

subwoofer ★★★★★ ()
Ответ на: комментарий от RazrFalcon

обычно при дизайне подобных бэдтрипов делают возможность установить onSet, onGet перехватчики которые могут чтото делать со знчением и релизуют геттер\сеттер когда он нужен

subwoofer ★★★★★ ()
Ответ на: комментарий от RazrFalcon

Вот так как-то.

#include <iostream>


template <typename T>
class Property {
private:
    Property & operator=(const Property &) = delete;
    Property(const Property &) = delete;
    Property() = delete;
protected:
    T value;
public:
    explicit Property(const T &value) {
        this->value = value;
    }

    const T& operator=(const T &value) {
        set(value);
        return this->value;
    }

    operator T() const {
        return get();
    }

    virtual void set(const T &value) {
        this->value = value;
    }

    virtual T get() const {
        return value;
    }
};

template <typename T>
std::ostream& operator<<(std::ostream &os, const Property<T> &prop) {
    os << static_cast<T>(prop);
    return os;
}

template <typename T>
std::istream& operator>>(std::istream &is, Property<T> &prop) {
    T val;
    is >> val;
    prop = val;
    return is;
}


class A {
public:
    class Val : public Property<int> {
    public:
        Val() : Property(0) {}

        void set(const int &value) {
            this->value = 2*value;
        }

        int get() const {
            return this->value;
        }
    } val;
};


int main() {
    A obj;
    std::cin >> obj.val;
    std::cout << obj.val;

    return 0;
}

Введём 10 — выведет 20.

evilface ★★ ()
Последнее исправление: evilface (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Показал примером. В принципе, отличается от шарповской реализации чуть большим визуальным шумом и необходимостью умолчательного значения. А в остальном использование такое же.

evilface ★★ ()

Не надо обижать хипстеров. Они такую херню не будут писать.

А так, не пытайся делать две разные вещи в одном методе. Если они разные, то и делать их надо в разных методах. По-моему у Саттера что-то было на эту тему в одной из его книг из серии С++ In-Depth.

dave ★★★★★ ()

Буду оригинален

Лучше оба!

Конечно шучу - первый вариант лучше. Сразу ясно какая функция что делает, да и производительность выше - нет проверки на -1. Во многих местах может оказаться критично.

robus ★★★★ ()
Ответ на: комментарий от subwoofer

Ну типа минимализма захотел, один метод, но в зависимости от того, задан его параметр или нет, у нас получается геттер или сеттер. По моим задумкам, вполне бы лаконично смотрелось в коде. Куча однотипной лапши типа: getX/setX, getLabel/setLabel, getSelected/setSelected... etc. казалась мне не очень симпатичным интерфейсом... потом меня отпустило :)

xterro ★★★★★ ()
Ответ на: комментарий от xterro

Это было бы более-менее осмысленно в языках с динамической типизацией. Вот так:

…
    def value(val=None):
        if val is None:
            return self.val
        self.val = val
…

ValueOwner.value(10)
a = ValueOwner.value()

И то только если val всегда предполагает хранение какого-то значения.
А в данном варианте это плохо. Далеко не всегда существует некое невалидное значение (как "-1").

evilface ★★ ()
Последнее исправление: evilface (всего исправлений: 2)
Ответ на: комментарий от evilface

В питоне есть сеттеры для этого

class ValueOwner:
     
    def __init__(self, value=None):
        self.__value = value
           
    @property
    def value(self):
        return self.__value
     
    @data.setter
    def value(self, value):
        self.__value = value

foo = ValueOwner()
foo.value = 10
a = foo.value

uralbash ()
Ответ на: комментарий от uralbash

Я знаю. Это пример, что вариант ТС там укладывается чуть больше.
Хотя даже если бы не было, я бы написал value() и setValue(), а не такое.

evilface ★★ ()

Второй вариант никто не посоветует. [/thread]

FIL ★★★★ ()
Ответ на: комментарий от evilface

Хотя даже если бы не было, я бы написал value() и setValue(), а не такое.

Почему? Из-за крестов головного мозга, или есть более серьёзные причины?

Esper ()
Ответ на: комментарий от Zenom

А при чём здесь динамическая типизация?

Можно положить, к примеру, None туда, где ждут инт а во время выполнения разбираться.

evilface ★★ ()
Ответ на: комментарий от Esper

Хотя даже если бы не было, я бы написал value() и setValue(), а не такое.


Почему? Из-за крестов головного мозга, или есть более серьёзные причины?



А что предложишь?

evilface ★★ ()
Ответ на: комментарий от RazrFalcon

Нужно расширить костыль использованием std::option =)

Слишком просто :)

andreyu ★★★★★ ()
Ответ на: комментарий от evilface

Property() = delete;

Это лишнее, вы уже объявили конструктор:

explicit Property(const T &value)

andreyu ★★★★★ ()
Ответ на: комментарий от evilface

Глупости. В динамических языках это тоже нафиг не упёрлось, да и свойства там одной левой делаются. Смысл?

anonymous ()
Ответ на: комментарий от evilface

А что предложишь?

class ValueOwner:
    def __init__(self, value=None):
        self.value = value

    value = None

foo = ValueOwner()
foo.value = 10
a = foo.value
Esper ()
Ответ на: комментарий от evilface

Со статической типизацией можно ждать параметром Option. Так получается даже более осмысленно, чем с динамикой. Т. е. всё равно жуткая наркомания, но хотя бы в сигнатуре отражена.

Zenom ★★★ ()

смысла нету в обеих конструкциях ибо они синтетические

anonymous ()
Ответ на: комментарий от Esper

Из-за крестов головного мозга, или есть более серьёзные причины?

А что тут специфично для крестов? Или речь об отсутствии «свойств» в языке?

DarkEld3r ★★★★★ ()

Для значений с булевой семантикой часто использую для читаемости следующую конструкцию:

class A{
public:
	void sel() { _is_sel = true; }

	void unsel() { _is_sel = false; }

	bool is_sel() const { return _is_sel; }

private:
	bool _is_sel;
};
anonymous ()
Ответ на: комментарий от yoghurt

Ну так все правильно же. И структуры вместо полей класса. Все как в школе учили на паскале.

leave ★★★★★ ()
Ответ на: комментарий от vertexua

А как же move semantics? B-}

Ну, там ещё можно идею развивать и допиливать. Только зачем? )

evilface ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.