Следует ли использовать открытые или частные переменные?

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

Я решил переписать этот код, чтобы использовать в основном только один тип. Но я не знаю, что я должен использовать (переменные, которые используются только для методов в одном объекте, всегда являются частными и не являются предметом этого вопроса).

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

7 ответов


private члены данных обычно считаются хорошими, потому что они обеспечивают инкапсуляцию.

предоставление геттеров и сеттеров для них нарушает эту инкапсуляцию, но это все равно лучше, чем public члены данных, потому что есть только одна точка доступа к этим данным.

Вы заметите это во время отладки. Если это личное, ты!--11-->знаю вы можете изменять только переменную внутри класса. Если это общедоступно, вам придется искать всю кодовую базу для где может быть изменен.

как можно запретить геттеры/сеттеры и сделать свойства private. Это следует принципу скрытия информации - вы не должны заботиться о том, какие свойства имеет класс. Она должна быть самодостаточной. Конечно, на практике это неосуществимо, и если это так, конструкция, которая следует за этим, будет более загромождена и сложнее поддерживать, чем та, которая этого не делает.

это, конечно, правило - например, я бы просто используйте struct (соответствует class С открытым доступом) для, скажем, простого класса точек:

struct Point2D
{
   double x;
   double y;
};

поскольку вы говорите, что знаете теорию, а другие ответы углубились в значение public/private, getters и setters, я хотел бы сосредоточиться на том, почему использовать аксессоры вместо создания общедоступных атрибутов (данные членов в C++).

представьте, что у вас есть грузовик класса в логистическом проекте:

class Truck {
public:
    double capacity;

    // lots of more things...
};

если вы североамериканец, вы, вероятно, будете использовать галлоны, чтобы представить емкость ваших грузовиков. Представьте, что ваш проект закончено, оно работает совершенно, хотя много сразу польз Truck::capacity готово. На самом деле, ваш проект становится успешным, поэтому какая-то европейская фирма просит вас адаптировать ваш проект к ним; к сожалению, проект должен использовать метрическую систему сейчас, поэтому литры вместо галлонов должны использоваться для емкости.

теперь, это может быть беспорядок. Конечно, можно было бы подготовить кодовую базу только для Северной Америки и кодовую базу только для Европы. Но это означает, что исправления ошибок должны применяется в двух разных источниках кода, и это решено неосуществимо.

решение состоит в том, чтобы создать возможность конфигурации в вашем проекте. Потребитель должен мочь установить галлоны или литры, вместо этого быть фикчированным, hardwired выбором галлонов.

С подходом, рассмотренным выше, это будет означать много работы, вам придется отслеживать все виды использования Truck::capacity, и решить, что с ними делать. Это, вероятно, будет означать изменение файлов по всему кодовая база. Предположим, в качестве альтернативы, что вы решили больше theoretic подход.

class Truck {
public:
    double getCapacity() const
        { return capacity; }

    // lots of more things...
private:
    double capacity;
};

возможное, альтернативное изменение не предполагает изменения интерфейса класса:

class Truck {
public:
    double getCapacity() const
        { if ( Configuration::Measure == Gallons ) {
            return capacity;
          } else {
             return ( capacity * 3.78 );
          }
        }


    // lots of more things...
private:
    double capacity;
};

(Пожалуйста, примите во внимание, что есть много способов сделать это, что один только одна возможность, и это только пример)

вам нужно будет создать глобальную конфигурацию служебного класса (но вы все равно должны были это сделать) и добавить include in truck.h на configuration.h, но это все локальные изменения, остальная часть вашей кодовой базы остается неизменной, что позволяет избежать потенциальных ошибок.

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

надеюсь, что это помогает.


нет жесткого правила, что должна быть частная/public или protected.

Это зависит от роли вашего класса и того, что он предлагает.

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

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

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

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

код C++ обычно не использует геттеры / сеттеры, когда они не обеспечивают реальную прибыль. Если вы разрабатываете проект на 1 000 000 строк с большим количеством модулей, которые должны быть как можно более независимыми, это может иметь смысл, но для большинства кода нормального размера вы пишете изо дня в день, они излишни.


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


общедоступные переменные обычно не поощряются, и лучшая форма - сделать все переменные частными и получить к ним доступ с помощью геттеров и сеттеров:

private int var;

public int getVar() {
  return var;
}

public void setVar(int _var) {
  var = _var;
}

современные IDE, такие как Eclipse и другие, помогают вам в этом, предоставляя такие функции, как "реализовать геттеры и сеттеры" и "инкапсулировать поле" (который заменяет все прямые аксессы переменных соответствующими вызовами геттера и сеттера).


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

Другим преимуществом геттеров и сеттеров является то, что если вы используете IDE (например, Eclipse или Netbeans), вы можете использовать функциональность IDE для поиска место в коде, где вызывается функция. Они обеспечивают видимость того, где часть данных в этом конкретном классе используется или изменяется. Кроме того, вы можете легко сделать доступ к потоку переменных-членов безопасным, имея внутренний мьютекс. Функции getter / setter будут захватывать этот мьютекс перед доступом или изменением переменной.

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