Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.223.115.21] |
|
Страницы: (6) « Первая ... 3 4 [5] 6 все ( Перейти к последнему сообщению ) |
Сообщ.
#61
,
|
|
|
Нет. Декларативная программа - это не описание процесса, это описание конечного результата.
|
Сообщ.
#62
,
|
|
|
Цитата D_KEY @ Ну в твоем синтетическом примере может и не полез бы, а в реальном декларативном коде не вознкает потребности в рефакторинге? Ну вот чуть менее синтетический: import Data.Function ((&)) import Text.Printf data SaleSum = SaleSum { totalCost :: Double, totalQuantity :: Int } class SaleSummary s where summary :: s -> SaleSum instance Semigroup SaleSum where (SaleSum c1 q1) <> (SaleSum c2 q2) = SaleSum (c1 + c2) (q1 + q2) instance Monoid SaleSum where mempty = SaleSum 0.0 0 instance Show SaleSum where show (SaleSum cost quantity) = "Total cost = " ++ printf "%.2f" cost ++ ", quantity = " ++ show quantity totalSummary :: SaleSummary s => [s] -> SaleSum totalSummary xs = xs & map summary & mconcat ---------------------------------------------------------------- data Sale = Sale { itemID :: String, price :: Double, quantity :: Int -- maybe some other fields } instance SaleSummary Sale where summary s = SaleSum (price s * fromIntegral (quantity s)) (quantity s) testSales = [ Sale "Foo" 0.20 4, Sale "Bar" 1.10 3 ] main = print $ totalSummary testSales — https://ideone.com/xcshAS Добавлено Чушь. В мире ФП у тебя осталась история, что стул был непокрашенный, так же, как и в реальном. |
Сообщ.
#63
,
|
|
|
Ты это знаешь только из-за того, что знаешь как оно реализовано. Затем, что ты этот метод вызываешь и действуешь исходя из возвращаемого этим методом значения. Цитата korvin @ Но можно canWithdraw вынести в Request как Lazy<Boolean>-поле, и снова мокать ничего не надо. Это поле надо будет как-то заполнить. Как ты собираешься это сделать? Не сделал. Ты сделал 3 проперти (которые на секундочку должны быть полноценными объектами, а еще точнее абстракциями) и добавил указатель на функцию/делегат, которую надо вызвать в submit. Если у тебя в классе Transfer будет 10 функций (submit, rollback, list, rejeckt, еще что-то) и все они будут что-то рассчитывать опираять на данные из source и/или destination, то тебе придется добавлять 10 функций твой Request. А самое смешное, что если выносить эти функции в Request, то поля source, destination и money становятся не нужны Цитата korvin @ Не надо, мне нужно знать только тип входных данных (Request) и тип выходных данных (Response), которые полностью описывают всё, что нужно. А еще Account и Money чтобы работа шла так, как было задумано, а не по умолчанию Цитата korvin @ вместо того, чтобы декомпозировать код и вынести логику retry в отдельный RetriableConnection с настраиваемой политикой, отдельно и независимо протестированную, Fester решил накостылять сомнительные тесты прям над бизнес-логикой, сохранив её сложность и зафиксировав эту сложность в спецификации (тесте). сорян конечно, но RetriableConnection тоже надо тестировать. |
Сообщ.
#64
,
|
|
|
Цитата Fester @ Ну есть скажем требование: "в случае неудачного соединения N-раз повторить повторить попытку, после чего сообщить пользователю об ощибке". Вполне себе нормальный сценарий, который тоже можно протестировать. GUI ты тоже юнит тестами тестишь? Вот есть скажем требование что размер формы должен быть 100x200, вполне себе нормальный сценарий, который тоже можно протестировать. Или скажем "в случае неудачного соединения N-раз должно выскакивать окно - "Подключение невозможно, проверьте параметры сети", которое должно располагаться по центру экрана". Вполне себе нормальный сценарий, который тоже можно протестировать. Ага? |
Сообщ.
#65
,
|
|
|
Цитата korvin @ Только в реальном мире это именно что история, объектами из которой ты не можешь воспользоваться, а в ФП вполне себе можешь ФП более высокая степень абстракции, чем императив, а значит дальше от реальности.Чушь. В мире ФП у тебя осталась история, что стул был непокрашенный, так же, как и в реальном. Но мы отклонились от темы. ФП и декларативное программирование ортогональные вещи на самом деле. |
Сообщ.
#66
,
|
|
|
Цитата Fester @ Ты это знаешь только из-за того, что знаешь как оно реализовано. В смысле «я знаю»? класс Request публичный с публичными полями. Все знают, как оно реализовано. Цитата Fester @ Затем, что ты этот метод вызываешь и действуешь исходя из возвращаемого этим методом значения. может мне ещё и JDK мокать? java.lang.Math.sin, например? А то ж вдруг я его вызываю, да синус считаю. Ты сам-то мокаешь стандартную библиотеку? Цитата Fester @ Это поле надо будет как-то заполнить. Как ты собираешься это сделать? Также как и любое другое поле: передам нужное мне значение в конструктор. Ты сходи перечитай мой пример, там в самом конце показано. Или ты не знаешь, как структурки создавать? У тебя что, одни int'ы и double'ы в коде? Не пробовал там struct Point { double x; double y; } написать? Compound data type называется. Цитата Fester @ (которые на секундочку должны быть полноценными объектами, а еще точнее абстракциями) С хера ли? Ты что, Егора Бугаенко начитался/насмотрелся? Plain data там, никаких объектов, никаких абстракций. Цитата Fester @ Если у тебя в классе Transfer будет 10 функций (submit, rollback, list, rejeckt, еще что-то) и все они будут что-то рассчитывать опираять на данные из source и/или destination, то тебе придется добавлять 10 функций твой Request. 1) у каждой функции своя область определения, значит у каждой будет свой Request 2) нафига перегружать модуль Transfer ещё десятком какимх-то функций? Про Single Responsibility Principle слышал? Цитата Fester @ А еще Account и Money чтобы работа шла так, как было задумано, а не по умолчанию Сходи int и (+) замокай, а то вдруг они как-то не так сработают и у тебя всё пойдёт не так как было задумано. И malloc заодно. Цитата applegame @ ФП и декларативное программирование ортогональные вещи на самом деле. Ортогональные. Но на ФП как-то код более декларативен получается по-умолчанию. |
Сообщ.
#67
,
|
|
|
Цитата korvin @ Ну не знаю. По моему все такое же описание процесса. Та же упомянутая тобой композиция функций отнюдь не декларативна. Ортогональные. Но на ФП как-то код более декларативен получается по-умолчанию. |
Сообщ.
#68
,
|
|
|
Цитата korvin @ В смысле «я знаю»? класс Request публичный с публичными полями. Все знают, как оно реализовано. Все знают публичные поля класса Request (имя и тип), но никто не знает какие из этих публичных полей используются функцией submit. А значит, для вызова submit тебе надо либо инициализировать все поля класса Request, либо знать какие поля класса Request использует submit, чтобы не создавать лишние объекты. Цитата korvin @ может мне ещё и JDK мокать? java.lang.Math.sin, например? java.lang.Math.sin мокать не нужно, а, скажем, доступ к файловой системе нужно. Цитата korvin @ А то ж вдруг я его вызываю, да синус считаю. Ты сам-то мокаешь стандартную библиотеку? Я мокаю то, что мне нужно. Например доступ к реестр или доступ к файлам - тоже вполне себе стандартные штуки. Цитата korvin @ Также как и любое другое поле: передам нужное мне значение в конструктор Ты понимаешь, что вместо вызова некой функции (в данном случае canWithdraw) ты предлагаешь создать некое поле, которое должно заполняться гезультатом работы этой функции. При этом мало того, что функцию все равно придется вызывать (в продуктивном коде), так еще у тебя и Request'e добавится куча полей. Цитата korvin @ С хера ли? Ты что, Егора Бугаенко начитался/насмотрелся? Plain data там, никаких объектов, никаких абстракций. Не знаю кто такой Егор Бугаенко. Никаких plain data там нет. Смотри свой собственный код, у тебя у Account есть как минимум две функции: hasSameOwnerAs и canWithdraw плюс к этому read-only проперти owner. Ясен хрен, что тут должна быть абстракция. Цитата korvin @ 1) у каждой функции своя область определения, значит у каждой будет свой Request Отлично! Т.е. Request - это тоже нифига не plain data объект! Это должен быть интерфейс типа public interface IRequest { Money Amount {get;} // тут хз, возможно тоже должна быть абстракция IAccount Source {get;} IAccount Destination {get;} Action CheckCompliance{ get; set; } } Цитата korvin @ 2) нафига перегружать модуль Transfer ещё десятком какимх-то функций? Про Single Responsibility Principle слышал? ingle Responsibility Principle ничего не говорит о том, что в классе должна быть одна функция. |
Сообщ.
#69
,
|
|
|
Цитата Fester @ Все знают публичные поля класса Request (имя и тип), но никто не знает какие из этих публичных полей используются функцией submit Все используются, именно поэтому Request является типом аргумента submit. Это её (submit) область определения. Цитата Fester @ а, скажем, доступ к файловой системе нужно. Конечно, ведь это внешний ресурс с побочными эффектами. Цитата Fester @ Ты понимаешь, что вместо вызова некой функции (в данном случае canWithdraw) ты предлагаешь создать некое поле, которое должно заполняться гезультатом работы этой функции. Ну тебе ж не нравилось, что я вызываю простую чистую функцию. Вот тебе альтернативное решение. Цитата Fester @ При этом мало того, что функцию все равно придется вызывать (в продуктивном коде) И что? Это дело этой функции, с точки зрения submit это просто bool. Цитата Fester @ так еще у тебя и Request'e добавится куча полей. Какая куча полей? Цитата Fester @ Смотри свой собственный код, у тебя у Account есть как минимум две функции: hasSameOwnerAs и canWithdraw И что? Если они будут внешние, что принципиально изменится? Ну вынеси их в статику class Account { public final ID id; public final Money balance; ... public static boolean haveSameOwners(Account a, Account b) { ... } public static boolean canWithdraw(Account a, Money amount) { ... } } Что-то поменялось? Цитата Fester @ Ясен хрен, что тут должна быть абстракция. с хера ли? Тебе что, инъекцию абстракций сделали? Цитата Fester @ Т.е. Request - это тоже нифига не plain data объект! откуда такой глупый вывод? Цитата Fester @ Это должен быть интерфейс типа Нет, не должен. Цитата Fester @ ingle Responsibility Principle ничего не говорит о том, что в классе должна быть одна функция. Не говорит. Он говорит, что в классе не должно быть десяток каких-то функций, не имеющих отношения к классу. |
Сообщ.
#70
,
|
|
|
Цитата korvin @ Все используются, именно поэтому Request является типом аргумента submit. Это её (submit) область определения. Значит ли это, что других функций в классе Transfer нет? Или все другие функции должны точно также как и submit использовать все поля Request'а? Цитата korvin @ Ну тебе ж не нравилось, что я вызываю простую чистую функцию. Вот тебе альтернативное решение. Ты это для меня делаешь? Я думал, что ты тут пытаешься избавиться от моков Цитата korvin @ И что? Если они будут внешние, что принципиально изменится? Изменится то, что ты уже 2-й раз переписываешь свой код и все это ради того, чтобы не было моков. При этом код не становится понятнее. Цитата korvin @ Ну вынеси их в статику Если в данном случае и можно вынести в статический метод (т.к. функция примитивна), то нельзя гаравтировать, что вынос в статику будет работать в любом случае. Цитата korvin @ Он говорит, что в классе не должно быть десяток каких-то функций, не имеющих отношения к классу. Ну значит у класса может быть больше одной функции. Слава богу! Осталось только уточнить, могут ли эти функции использовать разные данные... Или если у тебя 10 функций, то ты шлепаешь свой Request для каждой? Так чтобы каждая функция использовала все публичные поля своего Request'а? |
Сообщ.
#71
,
|
|
|
Цитата Fester @ Значит ли это, что других функций в классе Transfer нет? В моём — нет. Цитата Fester @ Или все другие функции должны точно также как и submit использовать все поля Request'а? Ты чем читал? У каждой функции свои область определения и область значений. Что не ясно в этой фразе? Цитата Fester @ Я думал, что ты тут пытаешься избавиться от моков У меня-то их и нет. Цитата Fester @ Изменится то, что ты уже 2-й раз переписываешь свой код и все это ради того, чтобы не было моков. Я не переписываю, я пытаюсь объяснить тебе, болезному, в чём разница между чистой функцией и объектом. Цитата Fester @ Если в данном случае и можно вынести в статический метод (т.к. функция примитивна), то нельзя гаравтировать, что вынос в статику будет работать в любом случае. С чего б вдруг? Цитата Fester @ Или если у тебя 10 функций, то ты шлепаешь свой Request для каждой? Если они требуют разные входные данные, то, естественно у них будут разные типы аргумента. А ты что, клепаешь God object'ы? Цитата Fester @ Так чтобы каждая функция использовала все публичные поля своего Request'а? В подавляющем большинстве случаев — да. Если функция не использует какие-то поля, то зачем эти поля нужны? Для красоты? |
Сообщ.
#72
,
|
|
|
Цитата korvin @ У меня-то их и нет. Ты пытаешься избавиться от них создавая левые параметры в Request'е Цитата korvin @ Я не переписываю, я пытаюсь объяснить тебе, болезному, в чём разница между чистой функцией и объектом. Функция, которая ничего не делает - это заглушка, т.е. фактически это и есть mock. Каждый объект, в котором тебе нужна "чистая функция" - это есть твой mock-объект. Цитата korvin @ Если они требуют разные входные данные, то, естественно у них будут разные типы аргумента. А ты что, клепаешь God object'ы? Если у меня есть сущность "пользователь", у которого есть фамилия, имя, день рождения, емыл и физический адрес (страна, город, индекс, улица, дом) и у меня есть 2 сервиса: 1) поздравляет пользователя с днем рождения по мылу и 2) отправляет новогоднюю открытку по почте, то я не буду шлепать 2 объекта так чтобы в одном были дата рождения и е-мыл, а в другом почтовый адрес. Я уж как-нибудь обойдуть одним объектом, который полностью описывает "пользователя" и не буду убиваться из-за некоторой избыточности. И нет, это не God object. Цитата korvin @ В подавляющем большинстве случаев — да. Если функция не использует какие-то поля, то зачем эти поля нужны? Для красоты? В подавляющем случае как раз нет и в 99% случаев функция не использует все поля. Просто из-за того, что никто не будет шлепать отдельный объект ради каждой функции. |
Сообщ.
#73
,
|
|
|
Цитата Fester @ Функция, которая ничего не делает - это заглушка, т.е. фактически это и есть mock. Каждый объект, в котором тебе нужна "чистая функция" - это есть твой mock-объект. Не совсем понял эту цитату. Ты точно понимаешь, что подразумевается под чистыми функциями(Pure function)? |
Сообщ.
#74
,
|
|
|
Некогда использовать такое. Надо баги фиксать
|
Сообщ.
#75
,
|
|
|
Цитата Wound @ Не совсем понял эту цитату. Я опирался на код: Цитата korvin @ final var response = subject.submit(new Transfer.Request( money, () -> sourceAccount, () -> destinationAccount, () -> { /* e.g. do nothing in this particular test case */ } )); В данном тесте - последний параметр - это заглушка и, соответственно Request - это мок-объект. |