Здравствуйте, yenik, Вы писали:
Y>Определяй интерфейс в той же сборке, и не потянет.
Это если есть доступ к исходному коду.
Y>Не факт. Если заменить myTypeInstance.DoThings() на doThings(myTypeInstance), тогда то на то и выходит.
не совсем, все же в этом случае зависимость односторонняя. в первом случае зависимость в обе стороны.
AA>>>Достаточно набора функций,
Y>>А если набор функций — это 10 функций? Не легче ли инжектировать 1 интерфейс?
AA>Уверены что в одном методе нужно 10 функций?
В одном методе вряд ли. Но мы же говорим не о методе, а о классе. И цепочка вызовов в этом классе может не заканчиваться, и этот набор функций может быть нужно передать дальше.
AA>Возможно следует использовать композицию для получения из 10-ти 1-й функции.
Также возможно, что эти функции нужны по отдельности.
Y>>Определяй интерфейс в той же сборке, и не потянет. AA>Это если есть доступ к исходному коду.
Y>>Не факт. Если заменить myTypeInstance.DoThings() на doThings(myTypeInstance), тогда то на то и выходит.
AA>не совсем, все же в этом случае зависимость односторонняя. в первом случае зависимость в обе стороны.
Не понял эту мысль. Одна сборка зависит от другой. Почему в обе стороны?
AA>>>Достаточно набора функций,
Y>>А если набор функций — это 10 функций? Не легче ли инжектировать 1 интерфейс?
S>Интерфейс нужно реализовать, что бы его инжектировать. А 10 функций можно взять готовых или скомбинировать их неким образом. S>Более того, 10 функций можно заменить структурой с 10ю полями и в отношении количества инжектируемых функций такая структура будет ничем не хуже 1-го интерфейса. А в некотором отношении даже лучше, т.к. структуры не нужно реализовывать, достаточно лишь инициировать члены.
Да, структура — это получше, чем 10 параметров. Но в итоге получается некое квази-ООП в стиле C с указателями на функции, в котором отсутствуют возможности ООП в стиле C++. От этого в своё время ушли, и прогрессивность такого подхода неочевидна. Хотя в каких-то случаях он может быть вполне уместен.
Необходимость реализовывать интерфейс, как всякое техническое решение, имеет плюсы и минусы. Плюс в том, что код сгруппирован, его легче поддерживать.
А вообще было бы прикольно, если бы C# позволял такие вещи:
interface IFoo
{
string GetA();
}
var a = "%$#@$%#%^";
var bar = new<IFoo>
{
GetA = () => { return a; }
}
Здравствуйте, yenik, Вы писали:
AA>>>>Достаточно набора функций,
Y>>>А если набор функций — это 10 функций? Не легче ли инжектировать 1 интерфейс?
S>>Интерфейс нужно реализовать, что бы его инжектировать. А 10 функций можно взять готовых или скомбинировать их неким образом. S>>Более того, 10 функций можно заменить структурой с 10ю полями и в отношении количества инжектируемых функций такая структура будет ничем не хуже 1-го интерфейса. А в некотором отношении даже лучше, т.к. структуры не нужно реализовывать, достаточно лишь инициировать члены.
Y>Да, структура — это получше, чем 10 параметров. Но в итоге получается некое квази-ООП в стиле C с указателями на функции, в котором отсутствуют возможности ООП в стиле C++. От этого в своё время ушли, и прогрессивность такого подхода неочевидна. Хотя в каких-то случаях он может быть вполне уместен.
Уместно это там, кде кучка функций не связана общим состоянием, как это в ООП. Т.е. мы не интерфейс изменяемого контейнера распилили и передаем эти функции отдельно, сгруппировав их в структуру. В таком случае преимущества над интерфейсом нет.
Но если в метод поиска пути в графе передаем функции для возврата смежных вершин и функцию для определения стоимости перехода, то такую пару в интерфейс объединять как раз не нужно.
Y>Необходимость реализовывать интерфейс, как всякое техническое решение, имеет плюсы и минусы. Плюс в том, что код сгруппирован, его легче поддерживать.
Y>А вообще было бы прикольно, если бы C# позволял такие вещи: Y>
Y>interface IFoo
Y>{
Y> string GetA();
Y>}
Y>var a = "%$#@$%#%^";
Y>var bar = new<IFoo>
Y>{
Y> GetA = () => { return a; }
Y>}
Y>
Здравствуйте, gyraboo, Вы писали:
G> Задачи типа многопоточности и распределённости не беру, т.к. тут всё понятно, ФП в этом изначально сильна.
Это миф, кстати. Многие ФЯзыки или до сих пор не умеют в многоядерность, или много лет не умели. У многих самая популярная структура данных — односвязный список, работу с которым параллелить довольно сложно. У флагмана ФП Хаскеля есть умение во многоядерность и есть разнообразие структур данных, но сборщик мусора ставит все потоки на паузу, порой надолго.
Здравствуйте, D. Mon, Вы писали:
DM>Это миф, кстати. Многие ФЯзыки или до сих пор не умеют в многоядерность, или много лет не умели. У многих самая популярная структура данных — односвязный список, работу с которым параллелить довольно сложно. У флагмана ФП Хаскеля есть умение во многоядерность и есть разнообразие структур данных, но сборщик мусора ставит все потоки на паузу, порой надолго.
Разве там не регулируется кол-во потоков как в ГО (1 поток на ядро)? В этом случае, как подсказывает интуиция, не должно быть проблем со сборкой мусора. если только сборщик не наткнулся на циклические ссылки. т.е. это возможно со сборщиком что-то не так.
Здравствуйте, varenikAA, Вы писали:
AA>Разве там не регулируется кол-во потоков как в ГО (1 поток на ядро)?
В Хаскеле есть и зеленые потоки, вроде горутин, и обычные системные. Наверняка регулируется. Но толку-то, если сборщику нужно старое поколение собирать, он все равно все остановит, ибо старое поколение кучи общее для всех потоков, а чистить параллельно, как в свежих версиях Го, он пока не умеет, вроде.
Здравствуйте, mrTwister, Вы писали:
T>1) ООП вынуждает привязывать методы к данным, хотя чаще всего в домене эта связь отсутствует. В результате, эта связь делается случайным образом, что приводит к каше в коде. Например, можно написать код в стиле ООП: "Холст.НарисуйКартину(кисть)", либо "Кисть.НарисуйКартину(холст)". Какой из этих двух вариантов правильный? Тут нет неправильного варианта. Реализован будет произвольный из них, смотря какая пятка зачесалась у программиста. Хотя в ФП стиле все однозначно: "НарисуйКартину(холст, кисть)". Нет никаких разночтений.
T>2) Данные жёстко связаны с методами. Какой бы из описанных в предыдущем пункте мы не выбрали вариант, мы будем жёстко связаны принятым случайным образом решением. Если мы выбираем вариант
"Холст.НарисуйКартину(кисть)", то НарисуйКартину жёстко привязана к холсту и мы сможем в дальнейшем рисовать картины исключительно только на холстах. В другом варианте мы будем жёстко привязаны к кисти и сможем рисовать только кистью. С другой стороны, при ФП декомпозиции, функция НарисуйКартину(где, чем) ни к чему не привязана и может рисовать на чём угодно и чем угодно, если это пригодно для рисования.
T>3) ООП провоцирует на создание большого количества лишних сущностей. Для борьбы с предыдущими проблемами приходится вводить большое количество искусственных сущностей, которые отсутствуют в исходном домене. Для этих сущностей часто даже невозможно придумать адекватного названия, вот и появляются всякие FooManager, BarHelper, все это обмазывается толстым слоем абстрактных фабрик, визиторов, репозиториев и прочих паттернов, которые для эксперта из доменной области звучат, как тарабарщина и мешают тем самым применению DDD. В результате программист вместо решения задачи из предметной области воюет с визиторами и декораторами.
Какие-то жабные стереотипы. Большинство языков на это не повелись, и в них все это легко и непринужденно решается свободными функциями. Да и в жабе это возможно, если рассматривать часть классов лишь как пространства имен.
Ничто не мешает на плюсах написать "НарисуйКартину(холст, кисть)", а ведь только на этом все эти претензии строятся
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, mrTwister, Вы писали:
T> На мой взгляд, если исключить неадекватов, лепящих монады и теории категорий во все дыры, то значительно проще.
T> 1) ООП вынуждает привязывать методы к данным, хотя чаще всего в домене эта связь отсутствует. В результате, эта связь делается случайным образом, что приводит к каше в коде. Например, можно написать код в стиле ООП: "Холст.НарисуйКартину(кисть)", либо "Кисть.НарисуйКартину(холст)". Какой из этих двух вариантов правильный? Тут нет неправильного варианта. Реализован будет произвольный из них, смотря какая пятка зачесалась у программиста. Хотя в ФП стиле все однозначно: "НарисуйКартину(холст, кисть)". Нет никаких разночтений.
Хрень какая-то. Выбор в данном случае будет основан на том кому нужен доступ к приватным членам и диспатчу по динамическому типу (т.е. виртуальный метод). Т.е. как раз те дыры, что пытаются заткнуть монадами и теориями категорий в ФП.
Иначе будет просто статическая функция как в ФП.
T> 2) Данные жёстко связаны с методами. Какой бы из описанных в предыдущем пункте мы не выбрали вариант, мы будем жёстко связаны принятым случайным образом решением.
Если не надо — не связывай, не будут. И решения случайные лучше не принимать. Да и не проблема это вовсе, есть рефакторинг, часто автоматический.
T> 3) ООП провоцирует на создание большого количества лишних сущностей. Для борьбы с предыдущими проблемами приходится вводить большое количество искусственных сущностей, которые отсутствуют в исходном домене. Для этих сущностей часто даже невозможно придумать адекватного названия, вот и появляются всякие FooManager, BarHelper, все это обмазывается толстым слоем абстрактных фабрик, визиторов, репозиториев и прочих паттернов, которые для эксперта из доменной области звучат, как тарабарщина и мешают тем самым применению DDD. В результате программист вместо решения задачи из предметной области воюет с визиторами и декораторами.
Паттерны в ФП тоже есть в достатке, просто они другие.
T> В результате, ФП проект со временем менее подвержен превращением в кашу. В фп стиле легче писать правильно и труднее говнокодить.
По-моему уже давно договорились, что оптимум: ООП на глобальном уровне дизайна, ФП — локально на уровне реализации тела методов.
Здравствуйте, gyraboo, Вы писали:
G>Здравствуйте, varenikAA, Вы писали:
AA>>Получается, что функции высшего порядка делают код чище и проще, и интерфейсы тут как бы не особо уже нужны.
G>Я вот тоже последнее время задаюсь вопросом, что если функциональное программирование лучше (в частности, для написания распределенных и многопоточных задач), то как в нём реализовать SOLID, GRASP, ООП-паттерны и прочие достижения ООП. Или же есть какая-то замена этим подходам, если да, то какая? В плане написания полноценного бизнес-приложения, как это делается например на Java/Spring. Пока что те книги, что я читал по ФП, полной картины не дают, и в своей работе ФП я пока применяю эпизодически, внутри классических ООП-обёрток и архитектуры, построенной по принципам SOLID и Ко. Возможно, в мире ФП пока эти принципы не выкристаллизовались ещё, или я просто не знаю где искать?
Подвернулась презентация. Сам не смотрел, но думаю, что там тема раскрыта. На сайте Влашина я давно пасусь. https://vimeo.com/113588389
Здравствуйте, mrTwister, Вы писали:
T>1) ООП вынуждает привязывать методы к данным, хотя чаще всего в домене эта связь отсутствует. В результате, эта связь делается случайным образом, что приводит к каше в коде. Например, можно написать код в стиле ООП: "Холст.НарисуйКартину(кисть)", либо "Кисть.НарисуйКартину(холст)". Какой из этих двух вариантов правильный? Тут нет неправильного варианта. Реализован будет произвольный из них, смотря какая пятка зачесалась у программиста. Хотя в ФП стиле все однозначно: "НарисуйКартину(холст, кисть)". Нет никаких разночтений.
А если сделать Картина.Нарисовать(где, чем)?
Тогда, как и в твоём примере мы будем жёстко привзавны. Но теперь к картине. То есть мы не сможем на холсте нарисовать не картину, а, например, рисунок
·>Хрень какая-то. Выбор в данном случае будет основан на том кому нужен доступ к приватным членам и диспатчу по динамическому типу (т.е. виртуальный метод). Т.е. как раз те дыры, что пытаются заткнуть монадами и теориями категорий в ФП.
Приватные данные есть не сами по себе, а появляются для конкретной реализации, и появляются они в том месте, где происходит реализация. То есть не приватные данные определяют реализацию, а наоборот.
По поводу диспатча — диспатч нужен не типу, а конкретному алгоритму, этот тип использующему. Но при этом задается диспатч в типе. То есть при проектировании типа нам надо иметь ввиду конкретный алгоритм. И если алгоритм немного меняется, либо надо написать немного другой алгоритм, то внезапно может выяснится, что диспатч надо делать иначе и наш старый код вместо переиспользования обрастает костылями, декораторами, адаптерами и стратегиями
Здравствуйте, DenisCh, Вы писали:
DC>А если сделать Картина.Нарисовать(где, чем)? DC>Тогда, как и в твоём примере мы будем жёстко привзавны. Но теперь к картине. То есть мы не сможем на холсте нарисовать не картину, а, например, рисунок
Только это будет не картина (картина получается в результате работы метода), а скорее функция рисования картины, которая на вход получает данные. Вот мы и пришли к функциональному программированию, когда есть функция и данные для нее, только в нагрузку получили еще кучу синтаксического оверхеда (тм) от ООП, когда вместо того, чтобы просто написать функцию мы пишем класс-функцию.
Здравствуйте, mrTwister, Вы писали:
T>Не жабные, а ООПэшные. Да, ООП можно не использовать.
Так ООП, ФП и т.п. — это сферовакуум, а в реальном мире есть вполне конкретные ЯП. И они часто поддерживают много парадигм как раз потому, что нет весомого повода загонять разработчика в какую-то одну.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, mrTwister, Вы писали:
T> ·>Хрень какая-то. Выбор в данном случае будет основан на том кому нужен доступ к приватным членам и диспатчу по динамическому типу (т.е. виртуальный метод). Т.е. как раз те дыры, что пытаются заткнуть монадами и теориями категорий в ФП. T> Приватные данные есть не сами по себе, а появляются для конкретной реализации, и появляются они в том месте, где происходит реализация. То есть не приватные данные определяют реализацию, а наоборот.
Это проблема курицы и яйца. Какая разница кто кого определяет?
Простой пример. Пусть у нас будет тип Стек. Его можно реализовать разными способами. Например, в виде списка и в виде массива. Приватными данными будут разные вещи. Например, указатель на голову и счётчик размера в первом случае; или массив и индекс в нём во втором случае.
Вот тут и диспатч, тут и приватные данные. Как это делать в ФП без монад и паттернов — неясно.
T> По поводу диспатча — диспатч нужен не типу, а конкретному алгоритму, этот тип использующему. Но при этом задается диспатч в типе. То есть при проектировании типа нам надо иметь ввиду конкретный алгоритм. И если алгоритм немного меняется, либо надо написать немного другой алгоритм, то внезапно может выяснится, что диспатч надо делать иначе и наш старый код вместо переиспользования обрастает костылями, декораторами, адаптерами и стратегиями
Или код рефакторится и пишется немного другой алгоритм... Ты как-то демонизируешь ООП.
Костылей в ФП тоже вагон и маленькая тележка.
Костыли это результат определённого процесса разработки, а не стиля программирования.