Появился небольшой диссонанс от нескольких принципов, кажется будто они немного противоречат друг другу. Вот у нас есть Item, Sword, HealthPotion и мы хотим добавить Junk. Когда мы оставляем пустой метод Use, мы нарушаем LSP, но при этом соблюдаем OCP (не изменили базовый, поменяли поведение производного). Когда выводим Use в отдельный интерфейс, то смогли соблюсти LSP, но полностью похерили OCP, изменив базовый класс. Получается, тут дело в том, что нам изначально нужно было писать, чтобы не нарушить оба этих принципа? Но разве такое вообще возможно предугадать на 100%, что в будущем потребуется поменять, а что нет? P.S. Ну и пользуясь случаем, хотел сказать спасибо за уроки) Отличная подача, прекрасное объяснение материала и грамотно подобранные мемы, ну красотища
Я считаю что OCP не противоречит принципу LSP. Всё как вы и сказали, хорошую архитектуру нужно стараться продумывать заранее, и пытаться предвидеть зоны для расширения. На этапе формирования архитектуры игры желательно понимать - может ли появиться новая категория предмета или нет. К тому же, OCP с моей точки зрения не жесткое правило "Не смей изменять написанный код" а скорее рекомендательная эффективная стратегия. "Вместо изменения, расширяй". В реальных проектах конечно есть место рефакторингу когда просто бульдозером перекорчёвывают несколько десятков скриптов и это совершенно нормально и такое рано или поздно случается. Так что я думаю принцип OCP можно озвучить так "Лучше расширять, чем изменять, но если уж совсем вилы, иногда приходится и изменять"
Вы не одиноки в своих сомнениях. Я тоже считаю что, OCP и LSP противоречат друг другу. Корень этого противоречия я вижу в том, что они, говоря про производные классы, используют разные модели: OCP -- расширение функционала, а LSP -- наследование (детализацию абстракции). Поэтому им вряд ли удастся одновременно следовать. Вот автор в своем примере перешел с наследования на расширение и LSP теперь не применим, поскольку мы не можем использовать производный класс на месте базового. Кстати, за кадром, и OCP пострадал. Раньше код использовал List, а теперь -- List. Тут автор писал, что OCP не жесткое правило. Я думаю, что весь SOLID, вопреки названию не жесткий. Рекомендации, не более.
Отличное видео. До этого сталкивался с непониманием проблемы, которую решает этот принцип. Спасибо за объяснение, теперь понимаю. Хотелось бы увидеть, как ты реализовал контекстное меню с действиями у разных предметов. Сам столкнулся с такой трудностью. Немного не понимаю, как обрабатывать в ui при наведении на разные предметы разные кнопки.
Спасибо, у меня есть планы написать вторую игру в рубрике "паттерны на практике". Там я хочу как раз сфокусироваться на сложных диалогах и предметах которые усиляют игрока. Всё это конечно потребует времени, но буду стараться)
Отличное видео, хотелось бы увидеть разбор более сложных кейсов с применением интерфейсов на практике в Unity. Насколько мне известно, там довольно много подводных камней, которые делают использование интерфейсов намного менее оправданным.
Спасибо! Я подумываю над тем, чтобы сделать видео либо с конкретными задачами, упражнениями по солиду, либо с разборов каких-то кусков кода с реальных проектов. Но пока все в набросках
Есть тоже прикольное видео на тему оружия и итемов "Bob Nystrom - Is There More to Game Architecture than ECS?" Там структура item содержит интерфейсы Attack, Use, Defence и если хочешь что бы твой предмет можно было юзать просто дай ему этот класс а если не хочешь то присвой условно NoneUse . В общем тоже советую к просмотру
Отличный ролик, но я не очень понимаю как использовать этот принцип, когда в проекте много интерфейсов. Допустим у меня есть класс BaseItem с методом Collect и интерфейсы в духе IUsable, IDiscardeable, IDestroyable, ISellable и тд. Производные класса BaseItem по идее могут наследовать все эти интерфейсы разом, но как тогда их хранить? создавать по списку на каждый интерфейс? или создать один список BaseItem и при помощи if esle проверять наследуется ли экземпляр класса от нужных интерфейсов?
скажите пожалста, а как после добавления интерфейса пройтись циклом по списку ? . . . только типизировать список . . . а по-другому нельзя без ????????
на 10:03 на видео как раз это и делается. Создается три объекта разных реализаций и они добавляются в список usableItems. И потом по этому списку мы идём циклом.
Можно, но нужно ли? Тут вопрос в разнице между абстрактным классом и интерфейсом. Если помимо метода Use вкупе идут какие-то параметры, то можно. А так особого смысла нет
вопрос такой . . . между 8:45 и 9:00 . . .цитата: "если поставить любой производный класс" . . . а куда "поставить" ? . . . тут наверное какой-то текст пропущен . . . прослушайте пожалуйста сами ваш текст
Цитата на 8:48 "И вот принцип LSP как раз говорит о том что нужно организовать архитектуру таким образом, чтобы если мы подставили ЛЮБОЙ производный класс, в том числе наш JunkItem, система должна отработать без поломок". Куда именно подставлять? Подставлять в систему где мы массово пробегаемся по предметам, причем к предметам мы обращаемся как к базовым классам. Чуть раньше в этом же видео я показывал скрипт где мы пробегались по видео (07:16 TestFirst и 08:18 TestSecond)
Блять, круто конечно...Но наследник IEnumerable ICollection является родителем ReadOnlyCollection в котором поля Add и Remove заткнуты исключениями. А это, типа, стандартная логика языка и нарушается LSP( ЛСП - Ползать)
@@sergeykazantsev1655 как по мне. наоборот интереснее когда я могу что-то делать. ну на это получу какое-то забавное сообщение, чем тупо отстуствие такой возможности
Ну вы как разработчик, а скорее даже как геймдизайнер вольны выбирать как вам больше нравится. Пример я привёл в рамках своей задачи, чтобы показать что такой случай может быть когда один из производных классов не корректно отрабатывает логику указанную в базовом классе и это и есть нарушение LSP Если у вас есть более наглядный пример нарушения LSP - я готов ознакомиться, никогда не против найти вариант получше, всё таки с момента видео год прошёл)
Невероятно доступное объяснение! Спасибо большое!
По мне дык самый понятный принцип. Когда-то давно сам пришёл к тому что нужно соблюдать эти правила
У меня аж флэшбэк к тем временам, когда я читал head first design patterns. Особенно про утку
Спасибо, легко и понятно. Жаль мало просмотров для того контента.Повезло, что попало в рекомендации !
Спасибо, да этому каналу вчера 2 недели исполнилось. Постараюсь наполнить его полезными уроками, авось со временем просмотры накапают)
Это потрясающе, спасибо большое!
Спасибо! Объясняете сильно лучше чем кто либо! Даже LSP и DIP стали понятней :)
Первое понятное объяснение. Спасибо огромное.
Спасибо тебе. Я и раньше использовал этот подход в своем коде, но сука не мог понять что такое LSP
Отличное видео! Спасибо. Вы очень хорошо и интересно доносите информацию.
очень хорошо объяснено, уверен новичкам все кристально понятно, лайк
Очень качественный видеоролик, помогает готовиться к экзаменам 👍👍
Великолепные объяснения принципов. Спасибо.
Появился небольшой диссонанс от нескольких принципов, кажется будто они немного противоречат друг другу. Вот у нас есть Item, Sword, HealthPotion и мы хотим добавить Junk. Когда мы оставляем пустой метод Use, мы нарушаем LSP, но при этом соблюдаем OCP (не изменили базовый, поменяли поведение производного). Когда выводим Use в отдельный интерфейс, то смогли соблюсти LSP, но полностью похерили OCP, изменив базовый класс.
Получается, тут дело в том, что нам изначально нужно было писать, чтобы не нарушить оба этих принципа? Но разве такое вообще возможно предугадать на 100%, что в будущем потребуется поменять, а что нет?
P.S. Ну и пользуясь случаем, хотел сказать спасибо за уроки) Отличная подача, прекрасное объяснение материала и грамотно подобранные мемы, ну красотища
Я считаю что OCP не противоречит принципу LSP. Всё как вы и сказали, хорошую архитектуру нужно стараться продумывать заранее, и пытаться предвидеть зоны для расширения. На этапе формирования архитектуры игры желательно понимать - может ли появиться новая категория предмета или нет.
К тому же, OCP с моей точки зрения не жесткое правило "Не смей изменять написанный код" а скорее рекомендательная эффективная стратегия. "Вместо изменения, расширяй". В реальных проектах конечно есть место рефакторингу когда просто бульдозером перекорчёвывают несколько десятков скриптов и это совершенно нормально и такое рано или поздно случается.
Так что я думаю принцип OCP можно озвучить так "Лучше расширять, чем изменять, но если уж совсем вилы, иногда приходится и изменять"
Вы не одиноки в своих сомнениях. Я тоже считаю что, OCP и LSP противоречат друг другу. Корень этого противоречия я вижу в том, что они, говоря про производные классы, используют разные модели: OCP -- расширение функционала, а LSP -- наследование (детализацию абстракции). Поэтому им вряд ли удастся одновременно следовать. Вот автор в своем примере перешел с наследования на расширение и LSP теперь не применим, поскольку мы не можем использовать производный класс на месте базового. Кстати, за кадром, и OCP пострадал. Раньше код использовал List, а теперь -- List.
Тут автор писал, что OCP не жесткое правило. Я думаю, что весь SOLID, вопреки названию не жесткий. Рекомендации, не более.
Отличное видео. До этого сталкивался с непониманием проблемы, которую решает этот принцип. Спасибо за объяснение, теперь понимаю. Хотелось бы увидеть, как ты реализовал контекстное меню с действиями у разных предметов. Сам столкнулся с такой трудностью. Немного не понимаю, как обрабатывать в ui при наведении на разные предметы разные кнопки.
Спасибо, у меня есть планы написать вторую игру в рубрике "паттерны на практике". Там я хочу как раз сфокусироваться на сложных диалогах и предметах которые усиляют игрока. Всё это конечно потребует времени, но буду стараться)
Большое спасибо за видосы, очень познавательно.
я вас обожаю ❤
Замечательный контент и канал
Спасибо!
Круть!
Какой же ты крутой!
Отличное видео, хотелось бы увидеть разбор более сложных кейсов с применением интерфейсов на практике в Unity. Насколько мне известно, там довольно много подводных камней, которые делают использование интерфейсов намного менее оправданным.
Спасибо! Я подумываю над тем, чтобы сделать видео либо с конкретными задачами, упражнениями по солиду, либо с разборов каких-то кусков кода с реальных проектов. Но пока все в набросках
Есть тоже прикольное видео на тему оружия и итемов "Bob Nystrom - Is There More to Game Architecture than ECS?" Там структура item содержит интерфейсы Attack, Use, Defence и если хочешь что бы твой предмет можно было юзать просто дай ему этот класс а если не хочешь то присвой условно NoneUse . В общем тоже советую к просмотру
спасибо, учту
Отличный ролик, но я не очень понимаю как использовать этот принцип, когда в проекте много интерфейсов. Допустим у меня есть класс BaseItem с методом Collect и интерфейсы в духе IUsable, IDiscardeable, IDestroyable, ISellable и тд. Производные класса BaseItem по идее могут наследовать все эти интерфейсы разом, но как тогда их хранить? создавать по списку на каждый интерфейс? или создать один список BaseItem и при помощи if esle проверять наследуется ли экземпляр класса от нужных интерфейсов?
Насколько я понимаю, да, создавать по списку на каждый интерфейс и создавать системы которые обрабатывают один конкретный тип
@@sergeykazantsev1655 Спасибо!
скажите пожалста, а как после добавления интерфейса пройтись циклом по списку ? . . . только типизировать список . . . а по-другому нельзя без ????????
на 10:03 на видео как раз это и делается. Создается три объекта разных реализаций и они добавляются в список usableItems. И потом по этому списку мы идём циклом.
а можно было вместо интерфейса сделать класс UsedItem, в него засовываем Use() из Item и уже от него наследоваться мечу и зелью?
Можно, но нужно ли? Тут вопрос в разнице между абстрактным классом и интерфейсом. Если помимо метода Use вкупе идут какие-то параметры, то можно. А так особого смысла нет
@@sergeykazantsev1655 тогда что лучше: один абстрактный класс с несколькими методами или на каждый метод по интерфейсу?
сам того не зная ISP использовал)
Типа того, да. Считаю, что правильный ответ - это поступать по ситуации. В зависимости что за поведение, есть ли какие-то свойства и тд.
@@sergeykazantsev1655 а вот это уже мидлом нужно быть, чтобы чувствовать
вопрос такой . . . между 8:45 и 9:00 . . .цитата: "если поставить любой производный класс" . . . а куда "поставить" ? . . . тут наверное какой-то текст пропущен . . . прослушайте пожалуйста сами ваш текст
Цитата на 8:48 "И вот принцип LSP как раз говорит о том что нужно организовать архитектуру таким образом, чтобы если мы подставили ЛЮБОЙ производный класс, в том числе наш JunkItem, система должна отработать без поломок".
Куда именно подставлять? Подставлять в систему где мы массово пробегаемся по предметам, причем к предметам мы обращаемся как к базовым классам.
Чуть раньше в этом же видео я показывал скрипт где мы пробегались по видео (07:16 TestFirst и 08:18 TestSecond)
@@sergeykazantsev1655 спасибо . . . за цитату
Блять, круто конечно...Но наследник IEnumerable ICollection является родителем ReadOnlyCollection в котором поля Add и Remove заткнуты исключениями. А это, типа, стандартная логика языка и нарушается LSP( ЛСП - Ползать)
Ну наверное read-only можно считать исключением, хотя и не буду отрицать что в некоторых случаях приходилось пренебрегать этим принципом
Что за странный пример? Use у мусорных предметов вполнее нужен. для вывода сообщение что игрок идиот
Ну это токсичненько писать игрокам что они идиоты) лучше сделать так чтобы механика use была полностью скрыта для мусорных предметов)
@@sergeykazantsev1655 как по мне. наоборот интереснее когда я могу что-то делать. ну на это получу какое-то забавное сообщение, чем тупо отстуствие такой возможности
Ну вы как разработчик, а скорее даже как геймдизайнер вольны выбирать как вам больше нравится.
Пример я привёл в рамках своей задачи, чтобы показать что такой случай может быть когда один из производных классов не корректно отрабатывает логику указанную в базовом классе и это и есть нарушение LSP
Если у вас есть более наглядный пример нарушения LSP - я готов ознакомиться, никогда не против найти вариант получше, всё таки с момента видео год прошёл)
Спасибо!