На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (6) « Первая ... 3 4 [5] 6  все  ( Перейти к последнему сообщению )  
> TDD vs не TDD
    Цитата korvin @
    Программа и есть описание процесса, выполненное в той или иной нотации/модели.
    Нет. Декларативная программа - это не описание процесса, это описание конечного результата.
      Цитата D_KEY @
      Ну в твоем синтетическом примере может и не полез бы, а в реальном декларативном коде не вознкает потребности в рефакторинге?

      Ну вот чуть менее синтетический:

      ExpandedWrap disabled
        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

      Добавлено
      Цитата applegame @
      в мире ФП у меня остался и покрашенный и непокрашенный

      Чушь. В мире ФП у тебя осталась история, что стул был непокрашенный, так же, как и в реальном.
      Сообщение отредактировано: korvin -
        Цитата korvin @
        Это pure data, там нечего мокать.

        Ты это знаешь только из-за того, что знаешь как оно реализовано.

        Цитата korvin @
        Если ты про canWithdraw, то это простой чистый метод, зачем его мокать?

        Затем, что ты этот метод вызываешь и действуешь исходя из возвращаемого этим методом значения.

        Цитата korvin @
        Но можно canWithdraw вынести в Request как Lazy<Boolean>-поле, и снова мокать ничего не надо.

        Это поле надо будет как-то заполнить. Как ты собираешься это сделать?

        Цитата korvin @
        Я их сделал явными и ограниченными.

        Не сделал. Ты сделал 3 проперти (которые на секундочку должны быть полноценными объектами, а еще точнее абстракциями) и добавил указатель на функцию/делегат, которую надо вызвать в submit. Если у тебя в классе Transfer будет 10 функций (submit, rollback, list, rejeckt, еще что-то) и все они будут что-то рассчитывать опираять на данные из source и/или destination, то тебе придется добавлять 10 функций твой Request. А самое смешное, что если выносить эти функции в Request, то поля source, destination и money становятся не нужны :D

        Цитата korvin @
        Не надо, мне нужно знать только тип входных данных (Request) и тип выходных данных (Response), которые полностью описывают всё, что нужно.

        А еще Account и Money чтобы работа шла так, как было задумано, а не по умолчанию :)

        Цитата korvin @
        вместо того, чтобы декомпозировать код и вынести логику retry в отдельный RetriableConnection с настраиваемой политикой, отдельно и независимо протестированную, Fester решил накостылять сомнительные тесты прям над бизнес-логикой, сохранив её сложность и зафиксировав эту сложность в спецификации (тесте).

        сорян конечно, но RetriableConnection тоже надо тестировать.
          Цитата Fester @
          Ну есть скажем требование: "в случае неудачного соединения N-раз повторить повторить попытку, после чего сообщить пользователю об ощибке". Вполне себе нормальный сценарий, который тоже можно протестировать.

          GUI ты тоже юнит тестами тестишь? Вот есть скажем требование что размер формы должен быть 100x200, вполне себе нормальный сценарий, который тоже можно протестировать. Или скажем "в случае неудачного соединения N-раз должно выскакивать окно - "Подключение невозможно, проверьте параметры сети", которое должно располагаться по центру экрана". Вполне себе нормальный сценарий, который тоже можно протестировать. Ага?
            Цитата korvin @
            Чушь. В мире ФП у тебя осталась история, что стул был непокрашенный, так же, как и в реальном.
            Только в реальном мире это именно что история, объектами из которой ты не можешь воспользоваться, а в ФП вполне себе можешь :) ФП более высокая степень абстракции, чем императив, а значит дальше от реальности.
            Но мы отклонились от темы. ФП и декларативное программирование ортогональные вещи на самом деле.
            Сообщение отредактировано: applegame -
              Цитата Fester @
              Ты это знаешь только из-за того, что знаешь как оно реализовано.

              В смысле «я знаю»? класс Request публичный с публичными полями. Все знают, как оно реализовано.

              Цитата Fester @
              Затем, что ты этот метод вызываешь и действуешь исходя из возвращаемого этим методом значения.

              :facepalm: может мне ещё и JDK мокать? java.lang.Math.sin, например? А то ж вдруг я его вызываю, да синус считаю. Ты сам-то мокаешь стандартную библиотеку?

              Цитата Fester @
              Это поле надо будет как-то заполнить. Как ты собираешься это сделать?

              Также как и любое другое поле: передам нужное мне значение в конструктор. Ты сходи перечитай мой пример, там в самом конце показано. Или ты не знаешь, как структурки создавать? У тебя что, одни int'ы и double'ы в коде? Не пробовал там
              ExpandedWrap disabled
                struct Point {
                    double x;
                    double y;
                }

              написать? Compound data type называется.

              Цитата Fester @
              (которые на секундочку должны быть полноценными объектами, а еще точнее абстракциями)

              С хера ли? Ты что, Егора Бугаенко начитался/насмотрелся? Plain data там, никаких объектов, никаких абстракций.

              Цитата Fester @
              Если у тебя в классе Transfer будет 10 функций (submit, rollback, list, rejeckt, еще что-то) и все они будут что-то рассчитывать опираять на данные из source и/или destination, то тебе придется добавлять 10 функций твой Request.

              :facepalm:
              1) у каждой функции своя область определения, значит у каждой будет свой Request
              2) нафига перегружать модуль Transfer ещё десятком какимх-то функций? Про Single Responsibility Principle слышал?

              Цитата Fester @
              А еще Account и Money чтобы работа шла так, как было задумано, а не по умолчанию

              :facepalm: Сходи int и (+) замокай, а то вдруг они как-то не так сработают и у тебя всё пойдёт не так как было задумано. И malloc заодно.

              Цитата applegame @
              ФП и декларативное программирование ортогональные вещи на самом деле.

              Ортогональные. Но на ФП как-то код более декларативен получается по-умолчанию.
                Цитата korvin @
                Ортогональные. Но на ФП как-то код более декларативен получается по-умолчанию.
                Ну не знаю. По моему все такое же описание процесса. Та же упомянутая тобой композиция функций отнюдь не декларативна.
                  Цитата 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 объект!
                  Это должен быть интерфейс типа
                  ExpandedWrap disabled
                    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 ничего не говорит о том, что в классе должна быть одна функция.
                    Цитата Fester @
                    Все знают публичные поля класса Request (имя и тип), но никто не знает какие из этих публичных полей используются функцией submit

                    Все используются, именно поэтому Request является типом аргумента submit. Это её (submit) область определения.

                    Цитата Fester @
                    а, скажем, доступ к файловой системе нужно.

                    Конечно, ведь это внешний ресурс с побочными эффектами.

                    Цитата Fester @
                    Ты понимаешь, что вместо вызова некой функции (в данном случае canWithdraw) ты предлагаешь создать некое поле, которое должно заполняться гезультатом работы этой функции.

                    Ну тебе ж не нравилось, что я вызываю простую чистую функцию. Вот тебе альтернативное решение.

                    Цитата Fester @
                    При этом мало того, что функцию все равно придется вызывать (в продуктивном коде)

                    И что? Это дело этой функции, с точки зрения submit это просто bool.

                    Цитата Fester @
                    так еще у тебя и Request'e добавится куча полей.

                    Какая куча полей?

                    Цитата Fester @
                    Смотри свой собственный код, у тебя у Account есть как минимум две функции: hasSameOwnerAs и canWithdraw

                    И что? Если они будут внешние, что принципиально изменится?

                    Ну вынеси их в статику
                    ExpandedWrap disabled
                      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 @
                    Ясен хрен, что тут должна быть абстракция.

                    :facepalm: с хера ли? Тебе что, инъекцию абстракций сделали?

                    Цитата Fester @
                    Т.е. Request - это тоже нифига не plain data объект!

                    :facepalm: откуда такой глупый вывод?

                    Цитата Fester @
                    Это должен быть интерфейс типа

                    Нет, не должен.

                    Цитата Fester @
                    ingle Responsibility Principle ничего не говорит о том, что в классе должна быть одна функция.

                    Не говорит. Он говорит, что в классе не должно быть десяток каких-то функций, не имеющих отношения к классу.
                      Цитата korvin @
                      Все используются, именно поэтому Request является типом аргумента submit. Это её (submit) область определения.

                      Значит ли это, что других функций в классе Transfer нет? Или все другие функции должны точно также как и submit использовать все поля Request'а?

                      Цитата korvin @
                      Ну тебе ж не нравилось, что я вызываю простую чистую функцию. Вот тебе альтернативное решение.

                      Ты это для меня делаешь? :lol: Я думал, что ты тут пытаешься избавиться от моков :lol:

                      Цитата korvin @
                      И что? Если они будут внешние, что принципиально изменится?

                      Изменится то, что ты уже 2-й раз переписываешь свой код и все это ради того, чтобы не было моков. При этом код не становится понятнее.

                      Цитата korvin @
                      Ну вынеси их в статику

                      Если в данном случае и можно вынести в статический метод (т.к. функция примитивна), то нельзя гаравтировать, что вынос в статику будет работать в любом случае.

                      Цитата korvin @
                      Он говорит, что в классе не должно быть десяток каких-то функций, не имеющих отношения к классу.

                      Ну значит у класса может быть больше одной функции. Слава богу! Осталось только уточнить, могут ли эти функции использовать разные данные... Или если у тебя 10 функций, то ты шлепаешь свой Request для каждой? Так чтобы каждая функция использовала все публичные поля своего Request'а?
                        Цитата Fester @
                        Значит ли это, что других функций в классе Transfer нет?

                        В моём — нет.

                        Цитата Fester @
                        Или все другие функции должны точно также как и submit использовать все поля Request'а?

                        Ты чем читал? У каждой функции свои область определения и область значений. Что не ясно в этой фразе?

                        Цитата Fester @
                        Я думал, что ты тут пытаешься избавиться от моков

                        У меня-то их и нет.

                        Цитата Fester @
                        Изменится то, что ты уже 2-й раз переписываешь свой код и все это ради того, чтобы не было моков.

                        Я не переписываю, я пытаюсь объяснить тебе, болезному, в чём разница между чистой функцией и объектом.

                        Цитата Fester @
                        Если в данном случае и можно вынести в статический метод (т.к. функция примитивна), то нельзя гаравтировать, что вынос в статику будет работать в любом случае.

                        С чего б вдруг?

                        Цитата Fester @
                        Или если у тебя 10 функций, то ты шлепаешь свой Request для каждой?

                        Если они требуют разные входные данные, то, естественно у них будут разные типы аргумента. А ты что, клепаешь God object'ы?

                        Цитата Fester @
                        Так чтобы каждая функция использовала все публичные поля своего Request'а?

                        В подавляющем большинстве случаев — да. Если функция не использует какие-то поля, то зачем эти поля нужны? Для красоты?
                        Сообщение отредактировано: korvin -
                          Цитата korvin @
                          У меня-то их и нет.

                          Ты пытаешься избавиться от них создавая левые параметры в Request'е :)

                          Цитата korvin @
                          Я не переписываю, я пытаюсь объяснить тебе, болезному, в чём разница между чистой функцией и объектом.

                          Функция, которая ничего не делает - это заглушка, т.е. фактически это и есть mock. Каждый объект, в котором тебе нужна "чистая функция" - это есть твой mock-объект.


                          Цитата korvin @
                          Если они требуют разные входные данные, то, естественно у них будут разные типы аргумента. А ты что, клепаешь God object'ы?

                          Если у меня есть сущность "пользователь", у которого есть фамилия, имя, день рождения, емыл и физический адрес (страна, город, индекс, улица, дом) и у меня есть 2 сервиса: 1) поздравляет пользователя с днем рождения по мылу и 2) отправляет новогоднюю открытку по почте, то я не буду шлепать 2 объекта так чтобы в одном были дата рождения и е-мыл, а в другом почтовый адрес. Я уж как-нибудь обойдуть одним объектом, который полностью описывает "пользователя" и не буду убиваться из-за некоторой избыточности. И нет, это не God object.

                          Цитата korvin @
                          В подавляющем большинстве случаев — да. Если функция не использует какие-то поля, то зачем эти поля нужны? Для красоты?

                          В подавляющем случае как раз нет и в 99% случаев функция не использует все поля. Просто из-за того, что никто не будет шлепать отдельный объект ради каждой функции.
                            Цитата Fester @
                            Функция, которая ничего не делает - это заглушка, т.е. фактически это и есть mock. Каждый объект, в котором тебе нужна "чистая функция" - это есть твой mock-объект.

                            Не совсем понял эту цитату. Ты точно понимаешь, что подразумевается под чистыми функциями(Pure function)?
                            Сообщение отредактировано: Wound -
                              Некогда использовать такое. Надо баги фиксать :lol:
                                Цитата Wound @
                                Не совсем понял эту цитату.

                                Я опирался на код:
                                Цитата korvin @
                                final var response = subject.submit(new Transfer.Request(
                                money,
                                () -> sourceAccount,
                                () -> destinationAccount,
                                () -> { /* e.g. do nothing in this particular test case */ }
                                ));

                                В данном тесте - последний параметр - это заглушка и, соответственно Request - это мок-объект.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0757 ]   [ 16 queries used ]   [ Generated: 26.04.24, 12:31 GMT ]