Если бы там были «double везде» то что это за дичь
" return arguments.length? (endAngle = typeof _ === «function»? _: constant(+_), arc): endAngle;"
т.е у нас появляются внезапно и функции аргументы, а еще это может быть объект с заданными полями или массив.
Так вот попробуйте сначала почитать исходники любого норм написанного проекта, да хоть d3, и увидите что там стремятся как раз явно описать типы. И да меня похоже многие не понимают, я в комментах и о пользовательских типах пишу, и их тоже прописывать лучше явно, что это Arc с такими-то полями, а не что это хз что.
Я не знаю кто рассматривает языки с динамической типизацией так что «Речь естественно идет только о базовых типах». Разумеется я подразумеваю все пользовательские типы, доменную модель приложения.
Но как раз в том же JS (без keyword class из нового стандарта и без TypeScript) там черт ногу сломит даже с пользовательскими типами
Но с этим категорически не согласен. Текущий мой проект невозможно было бы без типов построить. Там и комментарии есть. И тесты частично. Но суть в семантике. Ну забудьте — и будете тратить в разы больше времени на отладку и написание тестов и потом всеравно будут баги и потом по новой тесты, на то, что было бы легче ограничить «ожидается MaterialType а не произвольный int»
Кстати плюсую. Проектированием в комментариях я занимаюст тоже, например обозначить что «если тут null — значит рендерить не нужно» семантика, которую иначе очень сложно выразить. Аналогично про ед измерения вроде это килограммы а не граммы, или миллисекунды а не секунды.
Но проблема бывает когда не только типы не используются, и комментариев и других ограничителей тоже нет, и попробуйте догадаться что там за единица измерения (называть всегда millis суффикс если это милисекунды — не факт что удобно, а еще бывает и сложней)
Я могу ответить не на вопрос «как типы помогают находить ошибки в логике.»
а на вопрос «как типы помогают не допустить лишних ошибок в логике».
Если у меня UnityType { Pawn, King,… etc } это ТипФигуры в шахматах (или другой игре не суть) и я передаю его в нужный метод, как аргумент или возвращаю, то я уверен что тут ожидается оно, и есть смысл в этом. А если я обозначу int (или вобще без типа не суть) я буду лишнее время тратить чтобы найти где оно передается, что туда передается, и почему, и как обрабатывать, если я пишу новое, или ВАЖНЕЙ — РЕДАКТИРУЮ, я ЧИТАЮ код, и вижу ограничения. Они придуманы как раз чтобы в голове держать ТипыФигуры, а не «незнамо что». А когда enum таких становится десятки, то перепутать чтото компилятор уже поможет. И даже без компилятора, find usages.
Тоже самое касается классов, в которых есть семантика, я знаю что ожидается класс который будет делать тото, да хоть делегат / интерфейс с функцией, у которой строгое число и смысл аргументов, а не function (a, b) с неясным смыслом аргументов. Это даже к вопросу косвенному недавно вон написали «уже не нужны паттерны, если можно передать указатель на функцию» — да можно было еще с Си передать указатели, смысл в семантике. И чтении и поиске при разработке.
А НЕ write only Код потом write only Тесты.
**
«статическая типизация это 10%, а тесты это 90%.»
по этому — т.е вы предлагаете написать огромное кол-во тестов, которые надо будет тысячу раз перечитывать (а разработка требует такого, что функционал меняется часто, и во многих местах, я уже не помню как оно работает), вместо нормального использования типов? Вот мне тесты читать не надо чтоб понять что тут ожидается UnitType или MaterialType и т.п., и как оно себя поведет и какие в нем ограничения.
Может даже тестов еще и нет, их над написать. а функционал в разработке, и меняется, толку тесты писать еще не было, не дошло, бывает? Часто. Тут только типы и помогут. Вот потом вы тесты и напишете. Или вобще выкините функционал, т.к. над по-другому реализовать.
И да у меня даже страха нет, когда тестов нет, или вообще нет, или еще нет, не суть, а вот типы уже есть, и уже видны семантические ограничения.
Класс, единственное с чем вы не согласны это «нет int».
В компьютерной памяти, даже в интерпретаторе, не может «не быть int», можно либо представлять все байтами — что низкий уровень. Но мы же с вами говорим о Самантике. Оно либо целое число, уже приведенное, Из строки. Или это Функция принимающая int. Или любое другое число и не факт что уже округленное. Это семантика.
Но зато копипаст и дичь в духе
" return arguments.length? (endAngle = typeof _ === «function»? _: constant(+_), arc): endAngle;"
отличная. Тут автор d3 видимо с вами не согласен. Потому что он за каким то фигом проверяет function а в другом месте он может проверять на строку.
Давайте везде понатыкаем проверок на function. На всякий случай округлим числа, ведь у нас математика бывает целочисленная «по задаче надо округлить». А еще из строки будем переводить в int (parseInt и в таком духе) (вы наверное и не знали о ней? нафига вот ее придумали? строки везде? или объекты? да черт ногу сломит, но int у нас нету). И на всякий это в каждой функции сделаем. Лишь бы чтоб все можно было везде передать. Только не говорите что это не нужно. Тогда у вас все сломается, если «у нас нет int». А что у вас есть?
Ваши аргументы про «когда приспичит внести типы… да как в d3» относятся к использованию готового API / интерфейса. А попробуйте сам свой API / интерфейс для лругих создать, тогда и скажете что не надо это все.
О да я глянул только что вашу ссылку на arc github.com/d3/d3-shape/blob/master/src/arc.js
Это жесть, куча копипаста в духе
" return arguments.length? (endAngle = typeof _ === «function»? _: constant(+_), arc): endAngle;"
И всеравно не понятны типы возврата. Тут будет приведение к int или float будет или строка? И в куче аналогичных случаев тот же вопрос.
А когда возврат структуры — вам еще внутри функции покопаться
И только не говорите что неважно. Вы с таким же успехом тогда аргументируете к вопросу «вот вам массив байт — в нем что — целое число и вещественное и с какой точностью, и какая в нем endianness если он сериализован, и что там за переменные в ней и каков размер массива если это он». А вы еще на функции будете проверять (по духу d3). А в колбеках будете выяснять что у вас в аргументах — строки (которые легко уже превратяться в int или float по вашей надобностью) или JSON сериализованная форма (и какого объекта серилизованного) — что даже в этом случае — строка всеравно, а семантика разная. Ну не дураки же придумали семантику. Ну потратите лишнее время на выяснения.
**
function arc() {
var buffer,
r,
r0
r1
и
export default function() {
var innerRadius = arcInnerRadius,
outerRadius
Это и есть объект. И система типов вот она. Даже автор d3 вводит. Только костылями, без явного объявления class (как в подмножестве 2016 или 2015 года не помню уже)
вам аналогичный ответ — что я ответил ниже — про добавление свойства в объект. И да = объекта у вас не может не быть, он есть. Вы же в своей функции чтото делаете с каким-то переданным JS объектом, пусть он и без класса.
Был X,Y, надо Z добавить. Удачи добавлять. Искать не надо вам. Ну да без поиска попробуйте.
Текстовый поиск без самантики в 21 веке? Статические типы и семантика имени что это объект или класс или переменная — были придуманы как раз для облегчения работы.
И допустим я не знаю имя функции. Я знаю что у меня есть сущность у которой X,Y координаты, и мне надо Z добавить. Какую функцию мне искать? Все которые работают с объектом у которого есть X,Y, но пока нет Z. Без семантики — кучу головной боли.
И да такие задачи сплошь и рядом в реальной разработке. Пример с X,Y,Z для упрощения. Реально бывает кучу других задач по расширению кол-ва свойств у сущности. А если вы сущности не ввели явно, а держите их в голове — ну удачи пройтись поиском.
А еще Find Usages. Вот есть у меня в C# тип MySuperType, и надо мне найти где он юзается: где конструируется — в одном из case, в другом — вообще все использования, объявнения его как параметра в метод и как тип возврата.
Что я буду делать в JS? Т.е всеравно TypeScript вам тут нужен будет если проект не прототип, и даже не факт что все эти IDE современные по TS помогут вам сделать то же самое что с системой типов элеиментарно уже десятки лет IDE делает.
Вы же понимаете что чтобы написать эти тесты в JS (например) нужно приложить больше усилий и времени, на величину «проверки типов», по сравнению с вариантом «написать только тесты бизнес-логики» в варианте статической проверки типов.
Во 2х, я бы сказал что защищают они даже не в момент рантайма программы, а в момент думанья человеком. Вот я допустим читаю вашу JS прогграмму, я должен перечитать тысячи (потенциально) тестовых вариантов чтобы понять какой тип аргумента у вас в функцию передается и может ли там быть null и должен ли там быть int от 1 до 10 вместо enum общепринятого. Верно?
А еще мне документацию надо где-то отдельно от основного кода искать? Т.е не в коде я буду видеть
Move[] GetMoves (TypeOfShape) — для нормального ООП
а вот такое >>
function GetMoves (TypeOfShape)
причем тип возврата вообще не виден, и даже если задокументировать что вертает Move — вы уверены что это быстрей чем написать тип возврата? И тип аргумента?
Я начинал изучать программирование с StarCraft1 GUI, потом Warcraft3 (тоже с GUI) — там даже не код, а парадигма натаскивания / выбора из dropdown подходящего (ограниченного множества) — события и условия и действия.
В техникуме на программера изучал JS — это был 1й язык именно написания кода (а не GUI).
Потом был assembler :) круто показало внутрянку.
И всегда на протяжении кодинга хоть на JS хоть на asm, я думал как с точки зрения сущностей / объектов… у нас был преподаватель на объектах чуть помешанный, ну там «очередь», «слот очереди», «животное», хотя и делали то на массивах параллельных, ну типа x[], y[] вместо объекта Obj {x, y}
Т.е тут дело как это в голове уместить. И абстракции в духе «очередь» и «слот» и т.п. ложатся на мозг человека очень хорошо… уж не помню по каким результатам исследований, но чел не может в голове держать несвязанные разрозненные концепции — далеко не уедешь, надо пытаться свести к формальной модели с определенными входами и выходами, и разными уровнями абстракции… в том числе несколько абстракций нельзя держать, вроде как 7 шт и дале уже забываешь о других объектах…
Если же исходить не из ООП объектов (которые по опыту можно и в JS и а ASM даже, главное концепция в голове), а строить какие-то сотни функций (function DoX(a, b, c) в JS) то в них очень просто запутаться… помню писал и шашки, и крестики-нолики, шахматы начинал — все на JS, по опыту знаю что путался… в итоге всеравно к объектам приходил, и к разным уровням абстракции, вроде это «поле» а это «фигура» а функция построения возможных ходов принимает «тип фигуры» в шахматах например… на Си без плюсов а потом и на C++ переписывал шахматы и явно было видно что статические типы облегчают концепции, в плане возврата в этому. Да это не Write-only, а когда недели/месяцы пилишь прогу (пусть и учебную в начале) то облегчают.
А потом пришел к нормальному ООП в С++ и C# — это было круто и никогда не уйду от них в пользу динамических (уж не силен в терминологии, строгая там или нет и т.п свойства) как в JS.
Да даже на статических и ООП заточенных типа C# бывает код абы-как написан, не используя нормальные общепринятые концепции… это как описывать кучу функций, кучу get-set или public свойства что по сути одно и тоже, но без адекватного поведения внутри объекта, и инкапсуляции… т.е добавить в объект свойство — это прошлый век (начало программирования), надо в него еще поведение заложить и инкапсулировать (чтоб все кому не лень не пытались извне копипастить, а если этот функционал в подчинении класса он и должен быть в нем описан — так понятней где искать — потом, при чтении кода и доработках)… имхо от недостатка опыта, они не допоняли концепции объектов со свойствами и поведением.
Что до паттернов, которые типа унивесральные (даже к JS применимо есть паттерны), суть их в том чтобы другой чел (или ты через годы) вспомнил/понял как оно тут работает в соответствии с общепринятыми джентельменскими нормами… и не 100% программеров придерживаются, но они формально есть, все эти «State в динамических языках не нужны» от непонимания что есть самый State, т.е может у вас вся программа из иммутабельных объектов, а тут у вас есть класс который имеет внутренный вложенный private (или нет — в JS) класс и вот он мутабельный State т.е вы его там гоняете и собираете по колбекам (парсер какойто иерархии, да хоть XML/JSON/SVG для наглядности) и в итоге вот у вас StateOfParsingProcess — вот он формально есть. Даже если вы его не напишите в виде статически class StateOfParsingProcess и не обозначите private — он в вашей голове есть. Но вот напишете — потом вспомнить и вам же будет проще.
Какие-то люди могут не понять как запрограммить процесс парсинга какогото формата файла на JS (не говорю про какие-то спец инструменты вроде разбора грамматик и построяния деревьев и т.п), вот они придумаю свою реализацию на любом языке, и она будет не понятна другому кто будет читать, потому что не то что не очевидна (это тоже), в ней может и логики не быть, потому что ее и не рефакторили, и сделали «чтоб работало» / write-only. А комуто это читать и допиливать вероятно.
в языках типа JS, вы потом запутаетесь отслеживать где тут какой Function передается и куда, и отслеживать баги и самое главное вникать как же оно все работает, где вход где выход и т.п
не думайте что это автоматом удобно всегда. написать 1 раз и только автору понимать — да удобней. но поддерживать даже автору, а тем более новому человеку такой код — ад.
к тому же у вас разные поведения с разным числом аргументов могут быть. это уже Func<T,T2,T3> а гдето Func<T,T2> и т.п
если точно также все объединить под максимальное число аргументов (типо Func<T,T2,T3>) тогда у вас будут неиспользуемые аргументы в реализациях которым они не нужны — что тоже запутывает (типо читаешь код и думаешь — а зачем это суда передается), а если более явно определять где-то будет список Func, гдето список Func<T,T2> гдето 3х аргументов и т.п. то это уже не 1 Map а 3 штуки! И поиск надо будет делать динамически во всех Map, и добавлять только в конкретную. И т.п.
плюсую. Надо явно определить интерфейс ISwimingDuck, и только утки реально уменющие плавать туда передадутся. К тому же я писал в комменте выше, что
а репозиторий если уж динамически модифицируемый нужен, он не существует сам по себе, гдето юзер в UI / или api если это серв, — будет вызывать ваши методы, ему надо показать ваши объекты, что там у каждого объекта за список behaviour-ов, и что он не может делать, и это в коде также не составит труда определить явно через тот же список behaviours в каждом объекте и реверсного списка объектов которые имплементируют конкретный интерфейс (возможно по типу интерфейса или enum)
т.е. вот есть юзер, у него есть UI, в нем вам надо показать список уток которые этот интерфейс имплементируют, через реверс-маппинг т.е List и туда заносить только могущить плавать.
Другое дело что иногда если есть редактор, скажем для игры, юзер может сам настраивать свойства уток, возможность их делать или не делать что-то, то тут лучше пойдет компонентный подход, как в том же unity3d, хотя и там не идеально сделано.
Еще раз. Explicit принцип придумал не я, а Фаулер. Вы будете спорить с ним? О том что он не нужен? Чтобы всегда делать кучу неявного кода через указатели на методы? Серьезно?
Далее, конкретно по вашему заявлению об «BroilerDuck расширяет класс MallardDuck… У BroilerDuck будут свои подклассы и все они, вместе с BroilerDuck, летать не будут никогда.» это неправильный подход, не надо их наследовать.
Вы объявляете класс BroilerDuck implements IFlying, и никого от нее не наследуете. Если вы хотите иметь общую реализацию в каких-то классах, делайте через компонентное наследование.
«Как избавляться от принудительно унаследованных ненужных интерфейсов?» ну не наследоваться просто так.
www.martinfowler.com/ieeeSoftware/explicit.pdf
в архитектуре с кучей лямбд потом будет очень сложно разобраться, хотя я не против лямбд, но для статического поведения и улучшения читаемости лучше определять явные интерфейсы, именно с явным именем IFlying, IMoving, и давать их подклассам… а репозиторий если уж динамически модифицируемый нужен, он не существует сам по себе, гдето юзер в UI / или api если это серв, — будет вызывать ваши методы, ему надо показать ваши объекты, что там у каждого объекта за список behaviour-ов, и что он не может делать, и это в коде также не составит труда определить явно через тот же список behaviours в каждом объекте и реверсного списка объектов которые имплементируют конкретный интерфейс (возможно по типу интерфейса или enum)
" return arguments.length? (endAngle = typeof _ === «function»? _: constant(+_), arc): endAngle;"
т.е у нас появляются внезапно и функции аргументы, а еще это может быть объект с заданными полями или массив.
Так вот попробуйте сначала почитать исходники любого норм написанного проекта, да хоть d3, и увидите что там стремятся как раз явно описать типы. И да меня похоже многие не понимают, я в комментах и о пользовательских типах пишу, и их тоже прописывать лучше явно, что это Arc с такими-то полями, а не что это хз что.
Но как раз в том же JS (без keyword class из нового стандарта и без TypeScript) там черт ногу сломит даже с пользовательскими типами
Но проблема бывает когда не только типы не используются, и комментариев и других ограничителей тоже нет, и попробуйте догадаться что там за единица измерения (называть всегда millis суффикс если это милисекунды — не факт что удобно, а еще бывает и сложней)
а на вопрос «как типы помогают не допустить лишних ошибок в логике».
Если у меня UnityType { Pawn, King,… etc } это ТипФигуры в шахматах (или другой игре не суть) и я передаю его в нужный метод, как аргумент или возвращаю, то я уверен что тут ожидается оно, и есть смысл в этом. А если я обозначу int (или вобще без типа не суть) я буду лишнее время тратить чтобы найти где оно передается, что туда передается, и почему, и как обрабатывать, если я пишу новое, или ВАЖНЕЙ — РЕДАКТИРУЮ, я ЧИТАЮ код, и вижу ограничения. Они придуманы как раз чтобы в голове держать ТипыФигуры, а не «незнамо что». А когда enum таких становится десятки, то перепутать чтото компилятор уже поможет. И даже без компилятора, find usages.
Тоже самое касается классов, в которых есть семантика, я знаю что ожидается класс который будет делать тото, да хоть делегат / интерфейс с функцией, у которой строгое число и смысл аргументов, а не function (a, b) с неясным смыслом аргументов. Это даже к вопросу косвенному недавно вон написали «уже не нужны паттерны, если можно передать указатель на функцию» — да можно было еще с Си передать указатели, смысл в семантике. И чтении и поиске при разработке.
А НЕ write only Код потом write only Тесты.
**
«статическая типизация это 10%, а тесты это 90%.»
по этому — т.е вы предлагаете написать огромное кол-во тестов, которые надо будет тысячу раз перечитывать (а разработка требует такого, что функционал меняется часто, и во многих местах, я уже не помню как оно работает), вместо нормального использования типов? Вот мне тесты читать не надо чтоб понять что тут ожидается UnitType или MaterialType и т.п., и как оно себя поведет и какие в нем ограничения.
Может даже тестов еще и нет, их над написать. а функционал в разработке, и меняется, толку тесты писать еще не было, не дошло, бывает? Часто. Тут только типы и помогут. Вот потом вы тесты и напишете. Или вобще выкините функционал, т.к. над по-другому реализовать.
И да у меня даже страха нет, когда тестов нет, или вообще нет, или еще нет, не суть, а вот типы уже есть, и уже видны семантические ограничения.
В компьютерной памяти, даже в интерпретаторе, не может «не быть int», можно либо представлять все байтами — что низкий уровень. Но мы же с вами говорим о Самантике. Оно либо целое число, уже приведенное, Из строки. Или это Функция принимающая int. Или любое другое число и не факт что уже округленное. Это семантика.
Но зато копипаст и дичь в духе
" return arguments.length? (endAngle = typeof _ === «function»? _: constant(+_), arc): endAngle;"
отличная. Тут автор d3 видимо с вами не согласен. Потому что он за каким то фигом проверяет function а в другом месте он может проверять на строку.
Давайте везде понатыкаем проверок на function. На всякий случай округлим числа, ведь у нас математика бывает целочисленная «по задаче надо округлить». А еще из строки будем переводить в int (parseInt и в таком духе) (вы наверное и не знали о ней? нафига вот ее придумали? строки везде? или объекты? да черт ногу сломит, но int у нас нету). И на всякий это в каждой функции сделаем. Лишь бы чтоб все можно было везде передать. Только не говорите что это не нужно. Тогда у вас все сломается, если «у нас нет int». А что у вас есть?
О да я глянул только что вашу ссылку на arc
github.com/d3/d3-shape/blob/master/src/arc.js
Это жесть, куча копипаста в духе
" return arguments.length? (endAngle = typeof _ === «function»? _: constant(+_), arc): endAngle;"
И всеравно не понятны типы возврата. Тут будет приведение к int или float будет или строка? И в куче аналогичных случаев тот же вопрос.
А когда возврат структуры — вам еще внутри функции покопаться
И только не говорите что неважно. Вы с таким же успехом тогда аргументируете к вопросу «вот вам массив байт — в нем что — целое число и вещественное и с какой точностью, и какая в нем endianness если он сериализован, и что там за переменные в ней и каков размер массива если это он». А вы еще на функции будете проверять (по духу d3). А в колбеках будете выяснять что у вас в аргументах — строки (которые легко уже превратяться в int или float по вашей надобностью) или JSON сериализованная форма (и какого объекта серилизованного) — что даже в этом случае — строка всеравно, а семантика разная. Ну не дураки же придумали семантику. Ну потратите лишнее время на выяснения.
**
function arc() {
var buffer,
r,
r0
r1
и
export default function() {
var innerRadius = arcInnerRadius,
outerRadius
Это и есть объект. И система типов вот она. Даже автор d3 вводит. Только костылями, без явного объявления class (как в подмножестве 2016 или 2015 года не помню уже)
Был X,Y, надо Z добавить. Удачи добавлять. Искать не надо вам. Ну да без поиска попробуйте.
И допустим я не знаю имя функции. Я знаю что у меня есть сущность у которой X,Y координаты, и мне надо Z добавить. Какую функцию мне искать? Все которые работают с объектом у которого есть X,Y, но пока нет Z. Без семантики — кучу головной боли.
И да такие задачи сплошь и рядом в реальной разработке. Пример с X,Y,Z для упрощения. Реально бывает кучу других задач по расширению кол-ва свойств у сущности. А если вы сущности не ввели явно, а держите их в голове — ну удачи пройтись поиском.
Что я буду делать в JS? Т.е всеравно TypeScript вам тут нужен будет если проект не прототип, и даже не факт что все эти IDE современные по TS помогут вам сделать то же самое что с системой типов элеиментарно уже десятки лет IDE делает.
Во 2х, я бы сказал что защищают они даже не в момент рантайма программы, а в момент думанья человеком. Вот я допустим читаю вашу JS прогграмму, я должен перечитать тысячи (потенциально) тестовых вариантов чтобы понять какой тип аргумента у вас в функцию передается и может ли там быть null и должен ли там быть int от 1 до 10 вместо enum общепринятого. Верно?
А еще мне документацию надо где-то отдельно от основного кода искать? Т.е не в коде я буду видеть
Move[] GetMoves (TypeOfShape) — для нормального ООП
а вот такое >>
function GetMoves (TypeOfShape)
причем тип возврата вообще не виден, и даже если задокументировать что вертает Move — вы уверены что это быстрей чем написать тип возврата? И тип аргумента?
В техникуме на программера изучал JS — это был 1й язык именно написания кода (а не GUI).
Потом был assembler :) круто показало внутрянку.
И всегда на протяжении кодинга хоть на JS хоть на asm, я думал как с точки зрения сущностей / объектов… у нас был преподаватель на объектах чуть помешанный, ну там «очередь», «слот очереди», «животное», хотя и делали то на массивах параллельных, ну типа x[], y[] вместо объекта Obj {x, y}
Т.е тут дело как это в голове уместить. И абстракции в духе «очередь» и «слот» и т.п. ложатся на мозг человека очень хорошо… уж не помню по каким результатам исследований, но чел не может в голове держать несвязанные разрозненные концепции — далеко не уедешь, надо пытаться свести к формальной модели с определенными входами и выходами, и разными уровнями абстракции… в том числе несколько абстракций нельзя держать, вроде как 7 шт и дале уже забываешь о других объектах…
Если же исходить не из ООП объектов (которые по опыту можно и в JS и а ASM даже, главное концепция в голове), а строить какие-то сотни функций (function DoX(a, b, c) в JS) то в них очень просто запутаться… помню писал и шашки, и крестики-нолики, шахматы начинал — все на JS, по опыту знаю что путался… в итоге всеравно к объектам приходил, и к разным уровням абстракции, вроде это «поле» а это «фигура» а функция построения возможных ходов принимает «тип фигуры» в шахматах например… на Си без плюсов а потом и на C++ переписывал шахматы и явно было видно что статические типы облегчают концепции, в плане возврата в этому. Да это не Write-only, а когда недели/месяцы пилишь прогу (пусть и учебную в начале) то облегчают.
А потом пришел к нормальному ООП в С++ и C# — это было круто и никогда не уйду от них в пользу динамических (уж не силен в терминологии, строгая там или нет и т.п свойства) как в JS.
Да даже на статических и ООП заточенных типа C# бывает код абы-как написан, не используя нормальные общепринятые концепции… это как описывать кучу функций, кучу get-set или public свойства что по сути одно и тоже, но без адекватного поведения внутри объекта, и инкапсуляции… т.е добавить в объект свойство — это прошлый век (начало программирования), надо в него еще поведение заложить и инкапсулировать (чтоб все кому не лень не пытались извне копипастить, а если этот функционал в подчинении класса он и должен быть в нем описан — так понятней где искать — потом, при чтении кода и доработках)… имхо от недостатка опыта, они не допоняли концепции объектов со свойствами и поведением.
Что до паттернов, которые типа унивесральные (даже к JS применимо есть паттерны), суть их в том чтобы другой чел (или ты через годы) вспомнил/понял как оно тут работает в соответствии с общепринятыми джентельменскими нормами… и не 100% программеров придерживаются, но они формально есть, все эти «State в динамических языках не нужны» от непонимания что есть самый State, т.е может у вас вся программа из иммутабельных объектов, а тут у вас есть класс который имеет внутренный вложенный private (или нет — в JS) класс и вот он мутабельный State т.е вы его там гоняете и собираете по колбекам (парсер какойто иерархии, да хоть XML/JSON/SVG для наглядности) и в итоге вот у вас StateOfParsingProcess — вот он формально есть. Даже если вы его не напишите в виде статически class StateOfParsingProcess и не обозначите private — он в вашей голове есть. Но вот напишете — потом вспомнить и вам же будет проще.
Какие-то люди могут не понять как запрограммить процесс парсинга какогото формата файла на JS (не говорю про какие-то спец инструменты вроде разбора грамматик и построяния деревьев и т.п), вот они придумаю свою реализацию на любом языке, и она будет не понятна другому кто будет читать, потому что не то что не очевидна (это тоже), в ней может и логики не быть, потому что ее и не рефакторили, и сделали «чтоб работало» / write-only. А комуто это читать и допиливать вероятно.
не думайте что это автоматом удобно всегда. написать 1 раз и только автору понимать — да удобней. но поддерживать даже автору, а тем более новому человеку такой код — ад.
к тому же у вас разные поведения с разным числом аргументов могут быть. это уже Func<T,T2,T3> а гдето Func<T,T2> и т.п
если точно также все объединить под максимальное число аргументов (типо Func<T,T2,T3>) тогда у вас будут неиспользуемые аргументы в реализациях которым они не нужны — что тоже запутывает (типо читаешь код и думаешь — а зачем это суда передается), а если более явно определять где-то будет список Func, гдето список Func<T,T2> гдето 3х аргументов и т.п. то это уже не 1 Map а 3 штуки! И поиск надо будет делать динамически во всех Map, и добавлять только в конкретную. И т.п.
т.е. вот есть юзер, у него есть UI, в нем вам надо показать список уток которые этот интерфейс имплементируют, через реверс-маппинг т.е List и туда заносить только могущить плавать.
Другое дело что иногда если есть редактор, скажем для игры, юзер может сам настраивать свойства уток, возможность их делать или не делать что-то, то тут лучше пойдет компонентный подход, как в том же unity3d, хотя и там не идеально сделано.
Далее, конкретно по вашему заявлению об «BroilerDuck расширяет класс MallardDuck… У BroilerDuck будут свои подклассы и все они, вместе с BroilerDuck, летать не будут никогда.» это неправильный подход, не надо их наследовать.
Вы объявляете класс BroilerDuck implements IFlying, и никого от нее не наследуете. Если вы хотите иметь общую реализацию в каких-то классах, делайте через компонентное наследование.
«Как избавляться от принудительно унаследованных ненужных интерфейсов?» ну не наследоваться просто так.
в архитектуре с кучей лямбд потом будет очень сложно разобраться, хотя я не против лямбд, но для статического поведения и улучшения читаемости лучше определять явные интерфейсы, именно с явным именем IFlying, IMoving, и давать их подклассам… а репозиторий если уж динамически модифицируемый нужен, он не существует сам по себе, гдето юзер в UI / или api если это серв, — будет вызывать ваши методы, ему надо показать ваши объекты, что там у каждого объекта за список behaviour-ов, и что он не может делать, и это в коде также не составит труда определить явно через тот же список behaviours в каждом объекте и реверсного списка объектов которые имплементируют конкретный интерфейс (возможно по типу интерфейса или enum)