Как стать автором
Обновить

Комментарии 24

Если такие статьи помогут избежать появления в продакшене кучи плохого кода, то я только за. Для полноты картины не хватает тестов на код с if-чиками и на написанный по SOLID ;) Сразу станет понятно какие еще проблемы появятся.
Спасибо, это идея!

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

для того что-бы опытные скидывали её не опытным?

Опытный в значении больше стаж разве что, а не уровень экспертизы. Есть и по 10 лет работают, а уровень Джуна по коду

Как раз на подобных примерах легче понять суть ООП, так что пиши еще!
Спасибо, прочитал, раньше не попадалось)) Видимо это судьба: статьи об инверсии зависимости должны быть написаны каждым поколением снова и снова))
Кстати, рекомендую статью Евгения всем, кому нравится более спокойный стиль изложения. Также мне показалось интересным: взгляд под другим углом, другие акценты, более обширные и приближенные к UIViewController примеры.
вроде, DI обычно относят к инверсии контроля
Спасибо, да, у меня в комментарии ошибка по невнимательности. Прямо из начала упомянутой выше статьи Евгения в блоге RedMadRobot:
    «DI (внедрение зависимости, англ. Dependency injection)»
    «IoC (Инверсия управления, англ. Inversion of Control)»
Евгений как раз хорошо написал о различиях этих терминов и о том, почему они зачастую упоминаются вместе. У меня в статье больше про DI, конечно. IoC хоть и присутствует в «решении Василия», но явным образом об этом не сказано.
Статья отличная и обязательна к изучению независимо от уровня. Но есть на мой взгляд одна тонкость — архитектура должна быть гибкой только там, где это необходимо. Если человекомеч пишется один раз и этот код не меняется годами — то это корректное решение, и оверхедить его нет смысла.
Спасибо за юмор, поучительная история.
ЗЫ
Не сразу понял что protocol это interface.
На примере Swift, поэтому и протоколы)
Хочу побыть адвокатом дьявола.
Варинат 1. У вас была готовая игра, очень простая кототрую заказчик хотел выкинуть на рынок первым-но вдруг для добавления оружия вам понадобилось все переписать, полная регрессия-фича не легла на текущую архитектуру проекта, не повезло. Потом заказчик выпустил эту игру на рынок и навсегда забыл про нее. Да, вы сделали в полтора раза меньше игр за то же время, но зато у вас правильная архитектура!

Второй вариант-у вас правда нужно расширение функционала и вы решили что наследование-неправильно, лучше делать через DI. Вы создали класс героя, у которого есть шапка, броник, меч. После этого вы создали шапку, броник и меч как асбтракцию, через DI передали герою их. Да вы потратили много времени, но у вас для добавления нового героя нужно всего лишь создать новый броник, меч, шапку, самого героя, скиллы и передать это все хозяйство герою. Все очень расширяемо, но вот нюанс-вам нужны просто уникальные герои-ведь человек в начале игры выбирает между варваром с мечом и силой 10 и магом с посохом и интеллектом 25.
При наследовании вы бы переопределили вес, тип оружия и характеристики за 5 минут — наследуешся от героя, перепоределил аттаку, ловкость написал 15-и вор создан. С ДИ же пришлось создать нож с ловкостью 10 и шапку с ловкостью 5, нож с атакой 20, ну повезло-броник подошел от варвара.
А потом вам говорят -разбойник должен прыгать выше, оказывается что проблема в его весе суммарном-30 а надо 20. Ты пытаешься уменьшить вес всех предметов чтобы сумма сошлась, но оказывается что бронник от варвара весит 25, но прыгать он может потому что силы больше. Надо срочно релизить — ты добавляешь шапку с весом -10, кидаешь ее вору, ура, вор начинает высоко прыгать.
Потом игра опять расширяется и нужен еще один герой-ассасин-такой же, но с большей ловкостью и без броника. Ты создаешь героя с базовой ловкостью 20, инжектишь от вора кинжал, шапку, бронника нет, запускаешь игру и видишь как герой с весом -5 улетает в космос-ведь у этой шапки вес -10 для компенсации броника.

Тут надо понимать, что бывают игры где бегают "герой с мечом" и "герой с посохом", а бывают игры где бегают варвар и маг. Выбор между этими двумя вариантами определяется не ООП, а геймдизайном.

В описании второго варианта, на мой взгляд, есть пара изъянов:
1 (субъективный). Вы так говорите, как будто создание отдельных реализаций каждого из интерфейсов сильно дольше чем if-ы/наследование. Но с учетом мощи современных IDE, я не думаю, что разница значительна.
2. Если вору надо прыгать выше — надо реализовывать эту особенность в воре, зачем трогать одежду? Решение с невесомой шапкой изначально было обречено на провал
описании второго варианта, на мой взгляд, есть пара изъянов:
1 (субъективный). Вы так говорите, как будто создание отдельных реализаций каждого из интерфейсов сильно дольше чем if-ы/наследование. Но с учетом мощи современных IDE, я не думаю, что разница значительна.

Второй вариант — это фантазии на тему класс персонажа, от которого наследуются вор, варвар, маг. У варвара при этом реализуется меч, у мага-посох, у вора — кинжал.
Идея в том что если персонажи полностью независимы то нам не нужен меч-у нас есть просто другой герой. А в случае с DI у нас абстрактный герой, у которого есть отдельно оружие, шапка и бронник, для создания нового героя вы создаете класс каждого из предметов, для внесения изменения у вас надо пройтись по всем классам, а потом чтобы сумма этих параметров сошлась. А на практике вам никогда и не было нужно хранить все вещи-они все реализуется в реальности в одном экземпляре и для изменения свойств вам не нужны отдельные предметы-вам нужен цельный герой, со всеми его свойствами.
Если вору надо прыгать выше — надо реализовывать эту особенность в воре, зачем трогать одежду?

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

Нууу, тут Остапа немножко понесло)
Но вообще «хорошо быть богатым и красивым»-но когда у вас баг на проде или нужно срочно релизить подставляется костыль. Идея в том что такой же костыль в варианте с наследованием не возник бы-там вес был бы прописан у персонажа. Ситуация конечно гипотетическая, но тоже самое можно сказать и рассказ из статьи.
Идея что DI -не серебряная пуля, а ifы и наследование никак не однозначное зло, абстракции нужно выбирать с умом. И это правда сложная задача! Обычно для правильного создания всех абстракций нужно понимание бизнес логики и чего хочет бизнес в итоге.
В ситуации с персонажем нужно понимать что это за игра-в простой ходилки а-ля марио if вполне возможно что проще было бы поддерживать, в игре где есть выбор из 3 заданныхх персонажей вам скорее всего лучше применить наследование, а если у вас существует инвентарь, предметы то проще сразу создавать отдельные предметы и например через DI давать их стартовому персонажу.
Идея в том что если персонажи полностью независимы то нам не нужен меч-у нас есть просто другой герой.

В этом случае я соглашусь, что наследование подойдет. И здесь это следует из самой постановки задачи. В статье приведен пример персонажа, у которого меч изначально не вынесен в отдельную сущность. Меч выносится в отдельную сущность только после того, как возникла необходимость дать ему дубину. Если же задача не «дать этому персонажу дубину», а «сделать другой вид персонажа», то наследование подходит лучше. И этот случай не исключает DI, у этих отдельных персонажей все еще потенциально может быть больше одного оружия, которые все еще лучше реализовать через DI (в случае необходимости).
так прыжки реализованы в зависимости от веса, напрямую у героя нету этого свойства.

Если хватает времени — то надо добавить такое свойство, раз появилась такая необходимость. Если это требует больших изменений, а времени нет — то вес персонажа можно контролировать в самом методе получения веса, для этого не надо волшебных невесомых шапок.
Идея что DI -не серебряная пуля, а ifы и наследование никак не однозначное зло, абстракции нужно выбирать с умом. И это правда сложная задача! Обычно для правильного создания всех абстракций нужно понимание бизнес логики и чего хочет бизнес в итоге.

Это бесспорно. В статье и не говорится, что наследование или условные операторы — это однозначное зло. Там идет речь о том, что они не подходят для поставленной задачи. А вот понять, что за задача перед тобой стоит и что лучше подходит для ее решения — это, как вы и сказали, очень непросто и приходит с опытом.

Встречал такое хорошее утверждение, которое иллюстрируется этой статьей:


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


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


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

НЛО прилетело и опубликовало эту надпись здесь
идеалисты, которые начинают наперед продумывать все по максимуму
Приведите, пожалуйста, примеры из open source.
Написано отлично, доступно! В то же время, через повествование проскакивает важная мысль о том, что при прототипировании излишне любое поведение и взаимодействие оформлять протоколом, что тоже важно.
А потом Сова потребует, чтобы меч хорошо бил синих жаб, а дубина — зеленых обезьян, и все станет куда интереснее.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории