Ответ на Решетчатое наследование

в 13:38, , рубрики: иерархия, композиция, наследование, ненормальное программирование, ооп

Эта публикация — ответ на текст «Решетчатое наследование», опубликованный хабрапользователем potan, с предложением альтернативы, на субъективный взгляд автора, более близкой к ООП с классами.

«Целью наследование является привязка кода (набора методов) к минимальному набору свойств сущности (как правило — объекта), которые он обеспечивает и которые ему требуются».

Разве это так? К тому же в чём причина привязки? По каким признакам мы делаем вывод, что вот эти методы с этим объектом связать, а вот какие-то другие сделать частью других объектов, которые будут брать недостающие свойства/параметры через публичные интерфейсы?

Я придерживаюсь SOLID и идеи единственной ответственности на объект. Следовательно, иерархии наследования в таком случае — это уточнения ответственности к конкретным обстоятельствам, в которых ей надо исполняться.

Однако, объекты реального мира (либо предметной области) могут нести в себе несколько разных функций/ответственностей, поэтому прямого отображения их в систему классов может не получится. Такой объект в системе классов приложения я вижу как набор/контейнер из ответственностей, поддерживаемых объектом предметной области. Задача такого класса — ответственности между собой согласовывать. Согласование частично происходит через специфических потомков ответственностей, частично внутри класса контейнере.

Постараюсь привести пример близкий к примеру статьи.

Умения

  • умение стрелять
  • умение стрелять из ПЗРК
  • умение плавать
  • умение ходить

Умение — предоставляет общую информацию, например, связь с деревом умений и слот для предметов умения.

Умение стрелять — предоставляет возможность целиться, в слоте умений должно быть стреляющее оружие.

Умение стрелять из ПЗРК — уточняет реализацию функции целиться, например, экран ПЗРК рисует.

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

Кстати, вот тут я пропустил абстракцию — умение передвигаться, от которой унаследовать можно умения плавать и ходить.

Тогда конкретные персонажи будут содержать наборы умений.

Персонаж:

  • текущее умение передвигаться;
  • список умений: [].

Пехотинец (Персонаж):

  • текущее умение стрелять;
  • список умений: [плавать, ходить, стрелять из ПЗРК].

Утка (Персонаж):

  • список умений: [плавать].

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

Вопрос: что же будет, если данная набор будет разрастаться?
А как он может разрастаться?

  • добавить новое умение;
  • добавить нового персонажа;
  • добавить существующему персонажу умение, которое ранее ему было не свойственно (научить утку стрелять из ПЗРК).

Новое умение:
.Eсли это количественные вещи, например, ПЗРК стрелял с вероятностью попадания 0.6, а теперь 0.8 — так это просто разные записи в бд одного и того-же класса.

.Если что-то новое, например, меч — тогда в любом случае нужна гора кода, которая в начале владение мечом абстрактно опишет, а потом у каждого персонажа свои детали добавит. Хотя не обязательно сложная модификация (если абстракции Персонажа и его наследников хороши).

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

Сложнее существующему добавить ещё что-то, т.к. нужно внутренний код существующего перебирать заново. И возможно, проще будет унаследовать потомка.

Например, Утка нас всем устраивает, только вот стрелять не умеет. Значит, этот функционал надо наложить, может удобнее будет даже сразу 2 класса добавить: Простая Утка и Утка, умеющая стрелять. А в классе Утка оставить общие для дочерних уток ответственности — умение плавать — приспособленное к утке: в виде держаться на воде и нырять.

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

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

Автор: steb

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js