1. цикломатическая сложность определена только для структурных программ. Если в программе есть хоть что-то из: ФВП, виртуальные вызовы, указатели на функции, вычисляемые переходы и т.п. конструкции, то цикломатическая сложность становится неопределенной. Поэтому невозможно снизить цикломатическую сложность при помощи всяких filter/reduce, полиморфизма и других таких вещей. 2. цикломатическая сложность от использования ранних возвратов не уменьшается, а растет. Поэтому практически всегда ранние возвраты не рекомендуются - они существенно снижают читабельность кода и усложняют его поддержку. 3. Использование хештаблиц и любых других подобных подходов, естественно, ни как не снижает цикломатическую сложность.
Очень классное видео, которое будет полезным для начинающих. Автор красава. Единственный недочёт - последний пример не является реализацией паттерна Состояние. Данный паттерн предполагает, что состояние объекта может меняться. Логика изменения состояние описывается либо в классе, реализующий конкретное состояние, либо же в основном классе, в котором состояние меняется. А данный пример корректней было бы назвать реализацией паттерна Стратегия, только вместо функции, которая требует, чтобы класс реализовывал конкретный интерфейс(в нашем случае, классы наследуются от абстрактного класса ShapeState), используется класс Shape.
Пример с фигурами лучше было использовать для объяснения полиморфизма подтипов! Многоформенность! Формы фигур же! А вот способ доставки можно было использовать для State pattern, за исключением что там еще добавлен static Factory method (зачем?). С Value Object - придется вам перечитать теорию снова, и обратить внимание на слово equality и задуматься почему это так важно. Лайк, однозначно!
Есть легенда, что светлые программисты для замены цепочки if придумали switch, но перешедшие на тёмную сторону тимлиды стали распространять грязные слухи о безвкусии переключателя и о нём уже мало кто помнит. Я же скажу "Switch жив!"
@@ПавелФомин-ъ4с Все инструменты хороши, пока их можно применить. Никогда не видел код, который был бы хорош там, где его невозможно применить. В видео автор не избавлялся от ветвления, а делал его более читабельным для человека и более рациональным. Про return из разных мест рассказал, а вот про continue и break в циклах не стал упоменать.
Спасибо за видео. в блоке с полиморфизмом/стратегией мне не понятно вот что: в изначальной "плохой" реализации сущность сообщения у нас объект, что заставляет думать, что мы изначально не знаем, какой тип сообщения мы должны отослать, поэтому и пишем условия внутри функции. а во второй "правильной" реализации мы как будто уже знаем, какой тип сообщения хотим отослать, "оборачивая" строку нужным классом. Но как быть все-таки если мы не знаем тип сообщения заранее? Например, если нам пришел ответ с вот таким объектом, как в изначальном примере, и исходя из того, что у него внутри, нужно выбрать необходимую стратегию отсылки? Получается, опять те же if-ы использовать?
только хотел сказать что где же она чистая если у нее руки в 26 строке испачканы, а ты сам подправил. люблю когда возникает замечание, а автор сам тут же отвечает. как будто интерактив)0
последний пример я чет не особо и понял) например в первом варианте, где внутри класса иф-елсе в зависимости от передаваемого типа - так я могу например массив типов запустить в цикле и получить нужные значения, так как все ветвления в классе в решении же мне нужно создать класс под каждый тип и все равно нужно понимать какого класса передо мной объект, чтоб получить нужное значение типа в первом варианте я могу сделать так let result = {} types.forEach(type=>{ result[type] = new Shape(type) })// let a = result.circle.draw() ну или что то подобное во втором же мне придется делать то же самое ветвление, только не внутри класса, а уже внутри цикла, типа (if type == 'circle') result[type] = new Shape(new CircleState()) то есть ветвление ни куда не делось, а просто переходит на уровень выше?) и кода больше, мне как-то не ясна выгода хотя скорее всего я просто что-то не понял)
Сказки для самых маленьких. Есть сложность предметной области. Если к примеру у вас 3 типа договоров и 3 типа клиентов, то рано или поздно эти типы и связи между ними будут где-то фигурировать. Или в программе вы их внесете или в базу данных или в текстовый файл. Но есть принципиальные вещи, которые делают программу дурако-устойчивой или неустойчивой. Например если программист пишет if( type==1){ code1 } if( type==2){ code2 } то программа получается неустойчивой к появлению новых типов. Если админ добавит type==3, то программа посыпится с большой вероятностью. А когда кодовая база будет несколько гигабайт, то дырку найдут только через месяц, когда у какого нибудь клиента что-нибудь отвалится.
Сам реализовывал паттерн Стратегия, чтобы рендерить ячейки сложной таблицы в зависимости от входных данных, а потому пример со Стратегией понял, естественно, легко. Но вот чем точно отличается паттерн Состояние от Стратегии вообще не осознал :( UPD: Наверное, понял. Отличается тем, что в Состоянии мы прокидываем конкретный экземпляр класса в конструктор, а в Стратегии мы ВЫЗЫВАЕМ функцию, которая дергает нужный метод у экземляра того класса, который в эту функцию прокинут. Но вообще очень похожи два эти паттерна, вот прям когда лучше один, а когда другой не понимаю. Подскажите?
Паттерн Состояние достаточно специфичен, и область его применения определяется из названия. Принципиальное отличие от Стратегии заключается в том, что набор состояний и переходов между ними должны быть заранее определены, и для переходов эти состояния могут "знать" друг о друге. В данном видео это вообще никак не раскрыто. Более того, пример никакого отношения к паттерну Состояние не имеет.
@@alekseyilin6605 примерно разобрался, спасибо. Как я понял, с вызовом метода, Состояние меняет объект от которого может вызывать дальнейшие методы определенного ранее общего интерфейса.
|| "unknown command" что значит? Что на все другие команды, которых нет в ключах, будет давать результатunknown command? Что в это случае значит || ? Я думала это or, но не знала, что ее можно засунуть в print? Или я неправильно поняла?
а, ну это просто значит что если левая часть от || будет undefined то вернётся правая. Типа return commandActions[command] ? commandActions[command] : "Unknown command"; и тд по-разному можно это записать
Это не я так назвал. Не я это придумал) Разница между этими двумя паттернами незначительная. По сути паттерн состояние это продолжение паттерна стратегия. О тонкостях различия может сниму отдельное видео
Ты нмкак не уменьшаешь цикломатическую сложность переписыванием синтаксиса. 1) 1) Цикломатическую сложность определяется количеством тестов, которые тебе придется написать для твоего кода. Это написано в той статье, которую ты цитировал. Количество тестов, покрывающих все варианты у тебя не меняется. 2) Циклы не имеют никакого отношения к цикломатическую сложности, поскольку не создают новых вариантов выполнения.
@@easydev1205 Прочти внимательно ту статью в википедии, которую ты же сам и цитировал в видео. До конца и внимательно... Вместо того, чтобы упражняться в лже-вежливости.
Мда, ты просто рассказал про базовые принципы ооп и ещё пару очевидных вещей. Да, видео полезно прям совсем новичкам, но не очень понятно зачем оно нужно, ведь на ютубе и так тонна подобного материала
Несколько ретернов точно так же увеличивают сложность понимания программы, как и ветвления. Поскольку они никак не уменьшают количество ветвлений, они просто записывают их в другом виде
Не согласен. Поскольку эти ветвления выглядят как слои а не граф. Вы проходите сквозь текст спокойно отсеивая условия а не пытаясь понять логику ветвления.. Например, это очень хорошо использовать при вводе различных данных и их валидации
"Что, соответственно, минус" - ну и тут я уже слушать и перестал: во-первых, ну главное-то в коде всё ж не примитивность, а оптимальность, и - во-вторых - всё равно автомат на кучу состояний читаем куда лучше пятка раскиданных по коду IFов. Разбить функцию на кучу мелких - можно, конечно, заинлайнить пару-тройку совсем уж логически отвязанных от остального кода вещей (что нечасто и имеется) - но скорее всего у тебя просто получится убогое опенсорсное спагетти. Помните, дети: функции, классы и т.п. сделаны для того, чтобы применяться в разных частях программы по многу раз, и правя любую подобную штуку, редактирующий будет всё время отвлекаться на то, что же эта правка затронет - и всё ради того, чтоб увидеть (желательно, вообще в другом файле), что функция эта - очередной такой вот изврат. Едем дальше. Твоё "функциональное программирование" вообще к функциональному программированию отношения не имеет, вот совсем и никак. Почему ж вы все обычные процедуры обзываете "функциональным программированием" ?.. А так - да тут половина примеров была б нормальными, если б уже все поняли, что скобки после кондишнов надо на новую строчку переносить (тогда сразу видно, скоуп там, или просто одиночный вызов), и всякой чуши типа return; else не писали... Или (я просто на скриптах не писал давно) в отличае от нормальных C/С++, тут IF без скобок не работает?
@@easydev1205 Человек выразился не очень красиво и корректно, но в целом он прав. Функции с несколькими return ещё сложнее поддерживать и отлаживать, чем функции с многими if-else. Иначе говоря, Вы предложили избавляться от плохого подхода с помощью ещё более плохого подхода.
@@easydev1205 да считайте что угодно, но вы вряд ли потянете с ваши мнением на ученого-математика в области информатики и тому подобное, кому-то знаете ли и goto написать не зазорно. Советую ознакомиться с термином структурное программирование. А по поводу ранних выходов, если метод грубо говоря в десяток строк умещается, это еще терпимо, но многие их лепят черти знает как на несколько экранов, поэтому чем строже правила, тнм чище резкльтат в общем случае. И кстати return не в конце метода это тоже разновидность goto
к тернарному оператору и конкретному примеру с голосованием напрашивается такое: let age = 18 let canvote = age >= 18 && true console.log(canvote) не обзывайтесь только!
@@easydev1205 а что тут мудрого? вместо тернарного оператора тут простое логическое "и" - всегда возвращает или true или false, это удобнее, если тебе эти [yes,no] нужны не на вывод, а для дальнейшего использования.
Да, просто для такой простой логики должно быть каждому разработчику с первого взгляда понятно как это работает. Если это кого-то заставит призадуматься - я откажусь от такого решения. Это именно у меня такой подход. По крайней мере стараюсь так делать. Поэтому я, например, в своём коде такого точно использовать не буду. А там каждый сам решает
@@easydev1205 т.е. не все разработчики знают как работает "&&" и для чего нужен булев тип данных? в JS (да и в других языках) ведь очень много всего, что возвращает true или false. это простая, понятная и универсальная штука. разве нет?