Назначение частных участников в классе - PullRequest
24 голосов
/ 04 марта 2010

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

Ответы [ 10 ]

27 голосов
/ 04 марта 2010

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

class MyClass {
    public int foo;
}

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

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

Теперь, если вы понимаете, что foo должно быть на самом деле двойным, а не целым, вы измените его:

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 и т. д.

9 голосов
/ 04 марта 2010

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

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

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

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

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

7 голосов
/ 04 марта 2010

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

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

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

Приведенное выше объяснение объясняет, как использование 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);
5 голосов
/ 04 марта 2010

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

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

4 голосов
/ 04 марта 2010

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

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

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

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

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

3 голосов
/ 04 марта 2010

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

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

1 голос
/ 04 марта 2010

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

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

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

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

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

1 голос
/ 04 марта 2010

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

Точно!

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

1 голос
/ 04 марта 2010

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

0 голосов
/ 04 марта 2010

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

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

...