Set/get методы в С++

Java-программисты и API, похоже, предпочитают явные методы set/get.

однако у меня сложилось впечатление, что сообщество С++ хмурится такой практикой. Если это так, есть ли конкретная причина (помимо других строк кода), почему это так?

с другой стороны, почему сообщество Java предпочитает использовать методы, а не прямой доступ?

Спасибо

Ответ 1

Хорошо продуманный класс в идеале не должен иметь слишком много наборов и наборов. На мой взгляд, слишком много наборов и наборов в основном свидетельствуют о том, что кому-то другому (и, возможно, многим из них) нужны мои данные для достижения их цели. В таком случае, почему эти данные принадлежат мне в первую очередь? Это нарушает основной принцип инкапсуляции (операции с данными + в одной логической единице).

Итак, пока нет технических ограничений и (по сути, изобилия) методов "установить" и "получить", я бы сказал, что вам следует приостановить и пересмотреть свой дизайн, если вы хотите, чтобы слишком много таких "get" и " set 'в вашем интерфейсе класса, используемом слишком многими другими объектами в вашей системе.

Ответ 2

Есть случаи, когда геттеры/сеттеры подходят, но обилие геттеров/сеттеров обычно указывает на то, что ваш проект не достигает какого-либо более высокого уровня абстракции.

Как правило, лучше (в отношении инкапсуляции) демонстрировать операции более высокого уровня для ваших объектов, которые не делают реализацию очевидной для пользователя.

Некоторые другие возможные причины, почему это не так часто встречается в С++, как в Java:

  • Стандартная библиотека не использует его.
  • Bjarne Stroustrup выражает свою неприязнь к нему (последний абзац):

    Мне особенно не нравятся классы с много функций get и set. То есть часто свидетельствует о том, что он не должен были классом в первую очередь. Это просто структура данных. И если это действительно является структурой данных, сделайте ее структуры данных.

Ответ 3

Обычный аргумент метода get/set заключается в том, что если у вас есть оба, и они просто тривиальные return x; и x = y;, то вы вообще ничего не инкапсулировали; вы можете также просто сделать публичный член, который сохранит много шаблонов.

Очевидно, есть случаи, когда они все еще имеют смысл; если вам нужно сделать что-то особенное в них, или вам нужно использовать наследование или, в частности, интерфейсы.

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

Я не знал, что сообщество С++ нахмурило их больше или меньше, чем сообщество Java; Я считаю, что они менее распространены в таких языках, как Python, например.

Ответ 4

Я думаю, что причина, по которой сообщество С++ хмурится на геттерах и сеттерах, заключается в том, что С++ предлагает гораздо лучшие альтернативы. Например:

template <class T>
class DefaultPredicate
{
public:
  static bool CheckSetter (T value)
  {
    return true;
  }
  static void CheckGetter (T value)
  {
  }
};

template <class T, class Predicate = DefaultPredicate <T>>
class Property
{
public:
  operator T ()
  {
    Predicate::CheckGetter (m_storage);
    return m_storage;
  }
  Property <T, Predicate> &operator = (T rhs)
  {
    if (Predicate::CheckSetter (rhs))
    {
      m_storage = rhs;
    }
    return *this;
  }
private:
  T m_storage;
};

который затем можно использовать следующим образом:

class Test
{
public:
  Property <int> TestData;
  Property <int> MoreTestData;
};

int main ()
{
  Test
    test;

  test.TestData = 42;
  test.MoreTestData = 24;
  int value = test.TestData;
  bool check = test.TestData == test.MoreTestData;
}

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

class NoErrorHandler
{
public:
  static void SignalError (const char *const error)
  {
  }
};

class LogError
{
public:
  static void SignalError (const char *const error)
  {
    std::cout << error << std::endl;
  }
};

class Exception
{
public:
  Exception (const char *const message) :
    m_message (message)
  {
  }

  operator const char *const ()
  {
    return m_message;
  }

private:
  const char
    *const m_message;
};

class ThrowError
{
public:
  static void SignalError (const char *const error)
  {
    throw new Exception (error);
  }
};

template <class ErrorHandler = NoErrorHandler>
class RGBValuePredicate : public DefaultPredicate <int>
{
public:
  static bool CheckSetter (int rhs)
  {
    bool
      setter_ok = true;

    if (rhs < 0 || rhs > 255)
    {
      ErrorHandler::SignalError ("RGB value out of range.");
      setter_ok = false;
    }

    return setter_ok;
  }
};

и его можно использовать следующим образом:

class Test
{
public:
  Property <int, RGBValuePredicate <> > RGBValue1;
  Property <int, RGBValuePredicate <LogError> > RGBValue2;
  Property <int, RGBValuePredicate <ThrowError> > RGBValue3;
};

int main ()
{
  Test
    test;

  try
  {
    test.RGBValue1 = 4;
    test.RGBValue2 = 5;
    test.RGBValue3 = 6;
    test.RGBValue1 = 400;
    test.RGBValue2 = 500;
    test.RGBValue3 = -6;
  }
  catch (Exception *error)
  {
    std::cout << "Exception: " << *error << std::endl;
  }
}

Обратите внимание, что я также обработал неправильные значения параметром шаблона.

Используя это как отправную точку, его можно расширить разными способами.

Например, допустим, что хранилище свойства будет отличаться от общедоступного типа значения - поэтому RGBValue выше может использовать unsigned char для хранения, но интерфейс int.

Другой пример - изменить предикат так, чтобы он мог изменять значение setter. В RGBValue выше это можно использовать для фиксации значений в диапазоне от 0 до 255, а не для генерации ошибки.

Ответ 5

Нет ничего необычного в том, что в С++ есть явные методы set/get. Я видел это в большом количестве С++, может быть очень полезно не допускать прямой доступ к элементам данных.

Ответ 6

Свойства как концепция общего языка технически предшествуют С++, например. в Smalltalk, но они никогда не были частью стандарта. Геттеры и сеттеры были концепцией, используемой на С++, когда она использовалась для разработки пользовательского интерфейса, но, по правде говоря, это дорогое предложение для разработки пользовательского интерфейса в том, что фактически является системным языком. Общая проблема с геттерами и сеттерами на С++ заключалась в том, что, поскольку они не были стандартом, у всех был другой стандарт.

И в системных языках, где проблемы с эффективностью являются высокими, тогда просто становится проще публиковать эту переменную, хотя есть много литературы, которая сильно хмурится в этой практике. Часто вы просто видите более богатый обмен информацией между экземплярами объектов С++, чем простые элементы.

Вероятно, вы получите много точек зрения в ответ на этот вопрос, но в целом С++ должен был быть C, который делал объекты, делая OOP доступным для разработчиков, которые не знали объектов. Трудно было получить виртуальные и шаблоны на языке, и я думаю, что это какое-то время застоя.

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

Я признаю, что это в значительной степени мнение - в это время я использую С++ для высокооптимизированных материалов, таких как конвейеры 3D-графики - мне уже нужно управлять всей моей памятью объектов, поэтому я бы смутно рассмотрел принципиально бесполезный код, который просто служит для того, чтобы обернуть доступ к хранилищу в дополнительных функциях, - сказал он, что основные возможности производительности во время выполнения, такие как MSFT.net ILM, делают это положение, которое может быть трудно защитить время от времени

Чисто мое 2c

Ответ 7

Я не думаю, что сообщество С++ нахмурилось использовать геттеры и сеттеры. Они почти всегда являются хорошей идеей.

Ответ 8

Отметьте этот вопрос для объяснения того, почему Java предпочитает их, а причины для С++ одинаковы. Вкратце: он позволяет вам изменить способ доступа к элементам данных, не заставляя перекомпилировать код клиента (код, который использует ваш код). Это также позволяет вам применять определенную политику для доступа к данным и того, что делать, когда к этим данным обращаются.

Ответ 9

Применяя методы set/get, можно реализовать полезные побочные эффекты в getter/setter (например, когда аргумент get/set является объектом).

Ответ 10

Я удивлен, что никто не упомянул Java-интроспекцию и beans.

Использование get.../set... соглашение об именах в сочетании с интроспекцией допускает всевозможные умные хитрости с полезными классами.

Я лично считаю, что ключевого слова "public" должно быть достаточно, чтобы вызвать магию bean, но я не Ray Gosling.

Я считаю, что в С++ это довольно бессмысленное упражнение. Вы добавляете по меньшей мере шесть строк кода для тестирования и поддержки, которые не имеют никакой цели и будут по большей части игнорироваться компилятором. Это действительно не защищает ваш класс от злоупотреблений и злоупотреблений, если вы не добавили намного больше кодирования.

Ответ 11

Он имеет отношение к основам объектно-ориентированного программирования - скрытию внутренних объектов объекта от его пользователей. Пользователям объекта не нужно знать (и не заботиться) о внутренних объектах объекта.

Он также дает вам контроль над тем, что делается всякий раз, когда пользователь вашего объекта пытается его прочитать/записать. По сути, вы открываете интерфейс для пользователей объектов. Они должны использовать этот интерфейс, и вы контролируете, что происходит, когда вызываются методы в этом интерфейсе - геттеры и сеттеры будут частью интерфейса.

Это просто облегчает процесс отладки. Типичный сценарий - это когда ваш объект приземляется в странном состоянии, и вы отлаживаете его, чтобы узнать, как он туда попал. Все, что вы делаете, - это установить точки останова в ваших геттерах и сеттерах и предполагать, что все остальное в порядке, вы можете увидеть, как ваш объект попадает в странное состояние. Если ваши пользователи объектов напрямую обращаются к своим членам, выяснение, когда изменения состояния объекта становятся намного сложнее (хотя и не невозможно).

Ответ 12

Я бы сказал, что С++ требует getters/seters больше, чем Java.

В Java, если вы начинаете с открытого доступа к окну, а позже вы передумали, вместо этого вы хотите использовать getter/setter, очень просто найти все способы использования поля и реорганизовать их в getter/setter.

в С++, это не так просто. Язык слишком сложный, IDE просто не могут надежно сделать это.

поэтому в С++ вам лучше понять это в первый раз. В Java вы можете быть более предприимчивыми.

Ответ 13

До java были добавлены/установлены. Есть много причин использовать их, особенно, если вам нужно пересчитать sth. wenn меняется значение. Таким образом, первое большое преимущество заключается в том, что вы можете наблюдать за изменениями. Но imho его плохой, чтобы ВСЕГДА реализовывать get и set - часто получить достаточно. Другое дело, что изменения класса будут напрямую влиять на ваших клиентов. Вы не можете изменять имена участников, не заставляя рефакторинг кода клиентов публичными членами. Допустим, у вас есть объект с длиной, и вы меняете это имя члена... мм. С геттером вы просто меняете свою сторону кода, и клиент может хорошо спать. Добавление get/Sets для членов, которые должны быть скрыты, конечно же, нонсенс.