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

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

11 голосов
/ 03 сентября 2010

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

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

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

Спасибо

Ответы [ 13 ]

14 голосов
/ 03 сентября 2010

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

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

10 голосов
/ 03 сентября 2010

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

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

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

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

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

7 голосов
/ 03 сентября 2010

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

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

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

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

6 голосов
/ 03 сентября 2010

Я думаю, что сообщество 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;
}

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

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, а не для генерирования ошибки.

3 голосов
/ 03 сентября 2010

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

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

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

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

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

Чисто мой 2с

2 голосов
/ 03 сентября 2010

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

1 голос
/ 03 сентября 2010

Я удивлен, что никто не упомянул о самоанализе Java и бинах.

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

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

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

1 голос
/ 03 сентября 2010

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

1 голос
/ 03 сентября 2010

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

0 голосов
/ 03 сентября 2010

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

...