Почему стоит предпочесть использование модификатора доступа "private" вместо "public" везде?

Почему не рекомендуется постоянно использовать модификатор доступа <br/> public в Unity? Я никогда не задумывался об этом и хотел бы понять причины.
  • 3 сентября 2024 г. 7:52
Ответы на вопрос 4
Использование модификаторов доступа в программировании, включая разработку игр в Unity, очень важно для обеспечения гибкости, удобства сопровождения и защиты данных. Вот несколько причин, почему стоит предпочесть `private` и избегать использования `public` везде:

### 1. **Сокрытие реализации (Encapsulation)**:
Сокрытие данных и методов от внешнего доступа позволяет вам изменять реализацию класса, не нарушая работу других частей программы. Если вы используете `public`, другие объекты смогут взаимодействовать с вашими переменными напрямую, и любые изменения в коде могут привести к багам или непредвиденному поведению.

### 2. **Упрощение тестирования и отладки**:
Если класс содержит публичные поля, тестирование и отладка могут стать более сложными, так как любое изменение внешнего состояния может влиять на продолжение работы программы. С `private` полями вы можете контролировать, как и когда они изменяются, что облегчает нахождение и исправление ошибок.

### 3. **Читаемость и поддержка кода**:
Когда поля и методы светятся `public` без необходимости, это может привести к запутанности в коде. Когда вы видите использование `private`, это говорит о том, что программист задумался о внутренней архитектуре класса, что облегчает чтение и понимание кода.

### 4. **Управление состоянием объекта**:
Используя `private` для переменных, вы можете предоставлять публичные методы для работы с этими переменными. Это дает вам возможность добавлять валидацию или бизнес-логику, которая не позволила бы "неправильные" значения быть выставленными без контроля.

### 5. **Избежание зависимости**:
Если другой класс напрямую обращается к `public` переменной, он создаёт зависимость от внутренней структуры вашего класса. Если вы решите изменить эту структуру, вам нужно будет также изменить код всех классов, которые от неё зависят.

### 6. **Использование свойства (Properties)**:
C# поддерживает свойства, которые позволяют вам почти так же удобно использовать свои поля, как и публичные переменные, но с дополнительным контролем доступа и возможностью добавления логики при установке и получении значений.

### 7. **Взаимодействие с Unity и инспектором**:
В Unity вы можете использовать атрибут `[SerializeField]` для приватных полей, чтобы они были видимыми в инспекторе. Это позволяет вам сохранять преимущества инкапсуляции, оставаясь при этом удобным для настройки в редакторе.

Итак, хотя прямое использование `public` может упростить доступ, лучший подход — это всегда внимательно обдумать, какой именно уровень доступа лучше всего подходит для ваших данных и методов. Использование `private` в большинстве случаев направлено на улучшение качества, гибкости и надежности вашего кода.
Почему рекоменудется носить трусы? Чтобы жопа была в безопасности. 

Вот тут прибилзительно то же самое. Чтобы программисту не надо было думать как поведет себя код, если ВДРУГ кто то снаружи изменит то, что менять не надо вообще.
Когда вы делаете переменную публичной - вы тем самым говорите "эту переменную можно менять, я все предусмотрел, все будет ок. Это интерфейс взаимодействия - пользуйтесь". Но ведь далеко не для всех переменных класса НАДО предусматривать такое - зачем же делать лишнюю работу - ставим private и все, манипулируем переменной только изнутри, ЗНАЯ все варианты ее изменения.
Если без шибко умных слов, то приватными поля нужно делать, чтобы код отображал намерения разработчика и чтобы скрывать детали реализации (польза второго менее очевидна). 

Вот пример из комментариев о том, как делать не стоит:
class Character {
  public double health;
  public void TakeDamage(double damage) {
    if(damage <= 0) return;
    this.health -= damage;
    if(this.health <= 0) {
       this.Die();
    }
    // ...
  }
  public void Die() {
    this.DoSomethingSpecial();
    // ...
  }
  
  // Этот метод в будущем будет удалён, оставлен пока как костыль
  public void DoSomethingSpecial() {/*...*/}
}


Потом спустя хз сколько времени ты вернулся к коду.
Как ты будешь вспоминать, как правильно стоит наносить урон персонажам?
Через TakeDamage или в каких-то случаях можно напрямую изменить поле health?
Если в каких-то случаях нужно напрямую использовать поле health, то в каких?

Про скрытие деталей реализации:
Допустим, что ты решил добавить в игру поддержку модов.
Как разработчик мода поймёт, какие методы на персонажах можно вызывать не беспокоясь о том, что мод сломается в следующей версии игры?
Например без использования модификатора internal - разработчик мода может подумать, что этот метод можно вызывать, а по факту - в следующей версии ты, как разработчик игры, планируешь его удалить или изменить сигнатуру, что поломает мод.
+ если у тебя многие вещи сделаны через поля, то тогда ты не сможешь использовать интерфейсы (ну или их использование будет затруднено)
Одно слово - инкапсуляция. 
Если ты делаешь внутренний метод или переменную публично доступной, и работаешь с ней снаружи класса, то когда тебе понадобится изменить эту переменную или метод по работе с ней, тебе понадобится искать все использования этой переменной, а вот если ты заранее ограничишь себя, и для выхода наружу будешь специально создавать методы класса, то в тот момент, когда ты пожелаешь что либо изменить, тебе будет достаточно сделать это внутри класса.. да и искать влияние таких исправлений снаружи по ошибкам компилятора будет проще.

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

Тупой пример с координатами персонажа... делаешь эти переменные приватными, и оформляешь методы 'бег вперед', 'поворот' и т.п. а значит снаружи у тебя не будет чего то типа object.pos_x+=object.speed_x а будет object.moveRight().
Похожие вопросы