Вопрос о методах инкапсуляции и наследования - PullRequest
1 голос
/ 24 апреля 2020

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


Пример ситуации

Теперь представьте себе следующий сценарий, простая 8-битная игра, у нас есть куча различных объектов, таких как обычные коробки выступают в качестве препятствий, шипы , монеты , движущиеся платформы et c. Список может go on.

Все они имеют x и y координаты , прямоугольник, который определяет размер объекта, поле столкновения и a текстура . Также они могут совместно использовать такие функции, как установка положения , рендеринг , загрузка текстуры , проверка на столкновение et c.

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

Следовательно, базовый класс, такой как object, действительно может пригодиться, но для этого потребуется либо тонна getter - setters , либо наличие private членов, равных protected. В любом случае, компрометация компрометирует.


Учитывая анекдотический контекст, что было бы лучше:

1. Иметь общий базовый класс с общими функциями и члены, объявленные защищенными. Уметь использовать общие функции, передавать ссылку на базовый класс функциям, не являющимся членами, которым требуется только доступ к общим свойствам. Но компромиссная инкапсуляция.

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

3 . Лучший способ, о котором я не мог и подумать.


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

Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 24 апреля 2020

Основная цель «инкапсуляции» в объектно-ориентированном программировании состоит в том, чтобы ограничить прямой доступ к данным, чтобы минимизировать зависимости и, где зависимости должны существовать, express в терминах * 1003. * functions not data .

Это связано с Design by Contract , где вы разрешаете "publi c" доступ к определенным функциям и резервируете право произвольно изменять других в любое время, по любой причине, даже до точки их удаления, выражая их как «защищенные».

То есть, у вас может быть игровой объект, такой как:

class Enemy {
public:
  int getHealth() const;
}

Где функция getHealth() возвращает значение int, выражающее состояние здоровья. Как он получает это значение? Звонящий не должен знать или заботиться. Может быть, это байт 9 двоичного пакета, который вы только что получили. Может быть, это строка из JSON объекта. Это не имеет значения.

Самое главное, потому что не имеет значения, что вы свободны изменять то, как getHealth() работает внутри без взлома любого кода, который зависит

Однако, если вы выставляете свойство publi c int health, которое открывает целый мир проблем. Что, если этим манипулируют неправильно? Что если ему присвоено недопустимое значение? Как вы ловите доступ к этому свойству, которым манипулируют?

Гораздо проще, когда у вас есть setHealth(const int health), где вы можете делать такие вещи, как:

  • ограничить его до определенного диапазона
  • вызвать событие, когда оно превышает определенные границы
  • обновить сохраненное игровое состояние
  • передать обновление по сети
  • перехватить других «наблюдателей», которые могут потребоваться знать, когда этим значением манипулируют

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

protected - это не просто «сойти с газона», это важно инструмент для обеспечения правильного использования вашей реализации и по назначению .

1 голос
/ 24 апреля 2020

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

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

Иногда человек, которому нужно понимать ваш код, будет вами через год - так что имейте это в виду?

Я слышал, как люди говорили, что имеют защищенных членов вид ломает точку инкапсуляции

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

То, что определяет инкапсуляцию вашего объекта, - это предполагаемая спроецированная площадь поверхности API. Иногда этот элемент protected логически является частью поверхности - и это совершенно верно.

Если неправильно используется , protected члены могут предоставить клиентам доступ к изменяемым элементам, которые могут нарушить предполагаемые инварианты класса - что было бы плохо. Примером этого может быть, если вы смогли получить класс, выставляющий rectangle, и смогли установить ширину / высоту в отрицательное значение. Функции в базовом классе, такие как compute_area, могут внезапно привести к неправильным значениям и вызвать каскадные сбои, которые в противном случае должны были бы быть защищены от лучшего инкапсулирования.

Что касается дизайна вашего примера в вопрос:

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

Ваш пример звучит лучше для композиции с некоторыми небольшими интерфейсами:

  • Такие вещи, как point и тип vector будет базовым типом для создания композиций более высокого порядка, таких как rectangle.
  • Затем его можно объединить, чтобы создать model, который обрабатывает общие (логические) объекты в 2D-пространстве, которые сталкиваются с коллизиями.
  • logis пересечения / коллизии c может обрабатываться из внешний служебный класс
  • Визуализация может быть обработана из интерфейса renderable, где любой класс, который должен визуализироваться, выходит из этого интерфейса.
  • logi c для обработки пересечений может обрабатываться * Интерфейс 1048 *, который определяет поведение объекта на пересечении (эффективно абстрагирует каждый игровой объект в необработанное поведение)
  • et c
1 голос
/ 24 апреля 2020
Инкапсуляция

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

...