Комментарии 24
Надо еще пунктик добавить — Полезная статья и для опытных разработчиков.
Кстати, рекомендую статью Евгения всем, кому нравится более спокойный стиль изложения. Также мне показалось интересным: взгляд под другим углом, другие акценты, более обширные и приближенные к UIViewController примеры.
«DI (внедрение зависимости, англ. Dependency injection)»
«IoC (Инверсия управления, англ. Inversion of Control)»
Евгений как раз хорошо написал о различиях этих терминов и о том, почему они зачастую упоминаются вместе. У меня в статье больше про DI, конечно. IoC хоть и присутствует в «решении Василия», но явным образом об этом не сказано.
ЗЫ
Не сразу понял что protocol это interface.
Варинат 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ы и наследование никак не однозначное зло, абстракции нужно выбирать с умом. И это правда сложная задача! Обычно для правильного создания всех абстракций нужно понимание бизнес логики и чего хочет бизнес в итоге.
Это бесспорно. В статье и не говорится, что наследование или условные операторы — это однозначное зло. Там идет речь о том, что они не подходят для поставленной задачи. А вот понять, что за задача перед тобой стоит и что лучше подходит для ее решения — это, как вы и сказали, очень непросто и приходит с опытом.
Встречал такое хорошее утверждение, которое иллюстрируется этой статьей:
В программировании есть три возможных категории количества каких-то сущностей: одна, две и много, и все они принципиально отличаются.
Если у нас что-то одно (в данном случае — одно оружие) — делаем все максимально просто.
Если у нас чего-то два, то можно подпереть костылями и флажками, или банально ввести два поля в классе и выбирать ифом нужное. При этом как правило нет нужды делать сложную архитектуру, издержки на ее создание будут больше, чем профит. В статье — это как раз пример с енамом.
Если же чего-то больше чем два, то есть "много", то уже имеет смысл городить какое-то принципиально иное решение, так как с трех возможных вариантов количество потенциальных ошибок при использовании костыльных реализаций начинает очень быстро увеличиваться.
При этом нет ничего плохого в подпирании костылями, просто надо уметь четко различать эти ситуации и понимать, когда стоит закладывать расширяемую архитектуру, а когда — нет.
Архитектура для начинающих или почему не нужно вставлять флажок в человека-меча