Назначение частных участников в классе

Каковы цели наличия частных / защищенных членов класса / структуры в объектно-ориентированном программировании? Какой вред в том, чтобы все участники были публичными?

10 ответов

Решение

Инкапсуляция. Т.е. сокрытие реализации данных вашего класса. Это позволяет вам изменить его позже, не нарушая весь клиентский код. Например, если у вас есть

class MyClass {
    public int foo;
}

ваши клиенты могут написать код, как

MyClass bar = new MyClass();
bar.foo++;

теперь, если вы понимаете, что foo на самом деле должно быть double, а не int, вы меняете его

class MyClass {
    public double foo;
}

и код клиента не скомпилируется:-(

При хорошо спроектированном интерфейсе изменение внутренних элементов (частных частей) может даже включать превращение переменной-члена в вычисление или наоборот:

class Person {
    public String getName();
    public String getStreetAddress();
    public String getZipCode();
    public String getCountryCode();
    public int hashCode();
}

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

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

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

    public void setCountryCodeAndZip(String countryCode, String zipCode);

Однако с открытыми полями у вас просто нет этих вариантов.

Особый вариант использования частных полей - неизменяемые объекты; это очень распространено, например, на Java, примеры String а также BigDecimal, Эти классы вообще не имеют открытых сеттеров, что гарантирует, что их объекты, однажды созданные, не изменят своего состояния. Это позволяет оптимизировать производительность, а также облегчает их использование, например, в многопоточных программах, ORM и т. Д.

Вы можете прочитать тему " Скрытие информации" в Википедии.

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

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

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

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

Хорошо объяснено в Разделе 7.4: Защитите свои Частные Части этого онлайн-учебника по C++.

Зачем беспокоиться об этом?

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

Приведенное выше объяснение объясняет, как с помощью private облегчает обучение. Вот пример, который объясняет аспект "взлома кода":

Вот класс ParameterIO который читает и записывает вектор целочисленных параметров

class ParameterIO
{
public:
    // Main member
    vector<int> *Params;
    string param_path;

    // Generate path
    void GeneratePath()
    {       
        char szPath[MAX_PATH];
        sprintf(szPath,"params_%d.dat",Params->size());
        param_path = szPath;
    }

    // Write to file
    void WriteParams()
    {
        assert_this(!Params->empty(),"Parameter vector is empty!");
        ofstream fout(param_path.c_str());
        assert_this(!fout.fail(),"Unable to open file for writing ...");
        copy(Params->begin(),Params->end(),ostream_iterator<int>(fout,"\n"));
        fout.close();
    }

    // Read parameters
    void ReadParams(const size_t Param_Size)
    {
        // Get the path
        Params->resize(Param_Size);
        GeneratePath();
        // Read
        ifstream fin(param_path.c_str());
        assert_this(!fin.fail(),"Unable to open file for reading ...");
        // Temporary integer
        for(size_t i = 0; i < Params->size() && !fin.eof() ; ++i) fin>>(*Params)[i];
        fin.close();
    }

    // Constructor
    ParameterIO(vector<int> * params):Params(params)
    {
        GeneratePath();
    }

    // Destructor
    ~ParameterIO()
    {
    }      

    // Assert
    void assert_this(const bool assertion, string msg)
    {
        if(assertion == false) 
        {
            cout<<msg<<endl;
            exit(1);
        }
    }
};

Следующий код нарушает этот класс:

const size_t len = 20;
vector<int> dummy(len);
for(size_t i = 0; i < len; ++i) dummy[i] = static_cast<int>(i);
ParameterIO writer(&dummy);

// ParameterIO breaks here!
// param_path should be private because 
    // the design of ParameterIO requires a standardized path
writer.param_path = "my_cool_path.dat";
// Write parameters to custom path
writer.WriteParams();

vector<int> dunce;
ParameterIO reader(&dunce);
// There is no such file!
reader.ReadParams(len);

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

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

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

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

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

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

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

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

Не говоря уже о том, что вы, возможно, не хотите, чтобы другие классы имели возможность редактировать ваших участников вообще.

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

Никакого вреда в зависимости от аудитории и потребления класса. Позвольте мне повторить это еще раз, чтобы он погрузился.

Никакого вреда в зависимости от аудитории и потребления класса.

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

Поэтому вам действительно нужно посмотреть, как будет проходить урок и кем, прежде чем вы сможете начать отвечать на этот вопрос. Кроме того, как долго будет продолжаться жизненный цикл разработки программного обеспечения? Это месяцы? Много лет? Десятилетия? Будут ли другие люди, кроме вас, которые будут использовать этот класс?

Чем более "публичный" класс (т. Е. Чем больше людей будет его использовать и использовать), тем важнее будет создать надежный общедоступный интерфейс и придерживаться его.

Каковы цели наличия внутренних органов в организме человека? Какой вред в том, что все органы снаружи?

Именно так!

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

Краткий пример: вам может потребоваться обеспечить определенные условия для этого значения. В этом случае его установка может напрямую нарушить это условие.

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

Другие вопросы по тегам