методы set/get в C++

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

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

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

спасибо

13 ответов


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

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


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

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

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

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

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


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

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

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

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


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

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;
}

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

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 выше может использовать беззнаковый символ для хранения, но интерфейс int.

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


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

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

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

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

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

чисто мой 2c


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


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


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


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

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

Я лично чувствую, что ключевое слово "public" должно было быть достаточно, чтобы вызвать бобовую магию, но я не Рэй Гослинг.

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


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


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

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

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


Я бы сказал, что c++ нуждается в геттерах/сеттерах больше, чем Java.

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

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

Итак, в C++ вам лучше сделать это правильно в первый раз. В Java, вы можете быть более авантюрный.


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