Re[19]: своё vs. сторонее
От: fddima  
Дата: 25.10.13 21:16
Оценка: 22 (1) +2
Здравствуйте, ., Вы писали:

Ребята. Если вы не авторы PaypalCreditCardProcessor — то вам нефиг его тестить.
Если вы хоть кто-нибудь хоть когда-нибудь сталкивались с разработкой процессинга этих самых платежных карт — то вся ветка кажется смешной.
Если хочется написать стаб на него — это пожалуйста, только прийдётся перед этим хорошенько изучить реальный сервис, как он работает — иначе стаб будет врать. Кроме того, наличие тестов на стабах не отменяет тестов на тестовых окружениях. При чём up to 6 levels.
Как тут рядом IT — говорил — не то тестируете. Ну или не то обсуждаете. Мне лично пофигу, насколько процессор хорошо умеет посылать куда-нибудь запросы. Но мне жутко не пофигу, что комплекс отвечает бизнес требованиям. Особенно смешно это всё выглядит в купе с пробегавшим рядом примером с DateTime.Now, когда реально необходимо обрабатывать (откатывать) ну... переведя на простонародное холды, через десять дней, с учётом таблиц роутинга и кучи другой прочей херни, которая должна быть учтена именно временем транзации. DateTime.Now — это точно хрень, нет нигде такого. Ну а если вспомнить о offile/batch-процессинге документов — то now и так параметр как ни крути.
Всё вышесказанное не опровергает необходимость DI, но только лично в моей практике — это всё хрень. DI или не DI. Хотите реально протестировать скажем так CardProcessing, то вам прийдётся всегда иметь что-нибудь из этого, или всё сразу:
а) вам прийдётся общаться с ним чуть ли не на терминальном уровне
б) вам прийдётся иметь таки реальный процессинг и общаться с ним (и вас всегда ожидает эта стадия, т.к. реальные FI всегда это будут требовать)
в) для "долгоиграющих запросов", на подобии режекта холда — как-то это надо учитывать, ваши стубики тут ничем не помогут
г) вэлком в реальный мир. DI — нихера не помогает в тестировании.

PS: Как с эмулировать CardProcessing без DI? Слышал ли кто-нибудь о socket или http listener?
Re: своё vs. сторонее
От: fddima  
Дата: 25.10.13 21:23
Оценка:
Здравствуйте, CEMb, Вы писали:

Своё vs сторонее — посмотрите на TypeScript — модно, красиво, плагины к студии... но внтури — убожество.
Основная проблема JS -- отсутствие модульности. С TS — она как не была решена, так и её нет, под всяческими предлогами.
Я знаю как минимум 3 фрейворка где она решена. Dojo, Google Closure и приватный свой. Вот и думай — своё или стороннее.
При чём одно — не заменяет другое. Замкнутный круг... Но в целом — подход — хочешь хорошо — сделай сам — он как-то по жизни работает.
Re[22]: своё vs. сторонее
От: . Великобритания  
Дата: 25.10.13 21:38
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Это важно! Это очень важно. Если у вас только один транспорт, можно все заинлайнить. Это нормально.

Никто не спорит. Но допустим транспорт не один.

M>Если же транспортов несколько, возможны варианты. Либо все ваши транспорты (http/email/sms) — придатки к одному приложению.

Может к одному, может ко многим: одно http — в облаке летает, sms на спец сервере со спец железкой, gsm-модемом...

M>В этом случае они зависят не от конкретных служб, а от большого фасада Application, который предоставляет все возможные функции. Это вполне конкретный класс, который внутри реализует все, что нужно. Возможно — вызывая другие службы. Возможно — своими методами.

God object? Увольте. guice предлагает связываение делать в виде небольших обозримых модулей, с которыми можно делать композицию в бОльшие модули.

M>А еще есть ненулевая вероятность, что в случае нескольких транспортов BillingService.chargeOrder будет заинлайнен в соответствующие фронтенды. По одной банальной причине — это слишком общий интерфейс для биллинга. Для "onClick" пользователь может определяться авторизацией на сайте. Для sms — номером телефона. Как вы их будете в transactionLog представлять? А еще, вероятно, в бизнес-требованиях будет задача разделять различные источники заказов для построения статистики. Кто их будет строить? Как? Вы научите BillingService/TransactionLog различать разные типы пользователей из order? Или это будут разные логгеры (и разные billing service)?

Для примера с пиццей вполне сойдёт. Сделаешь UserDetails объект, который будет иметь credentials и т.п. В общем к теме это уже не относится.

M>При инлайне в обработчики все станет гораздо лучше. Мы будем иметь нужный тип логгера в обработчике. Вот CreditCardProcessor может быть заинжектен в разные реализации, он имеет смысл. А BillingProcessor — слишком тривиален и слишком нестабилен по функциональности. А еще плохо расширяем. Что, если одним из способов платежа для sms будет списание с баланса телефона? Для sms-гейта это будет вызов другого "payment provider" (с другим интерфейсом), а основная обвязка вроде логирования останется общая.

Да пример это только. Ну подразумевают, что у них только кредитками платят. Не знают американцы, что смс-ками можно делать оплату, это российская специфика. Не выводят они общую теорию всего, а просто простой и понятный примерчик без наворотов.

M>И еще один интересный факт. Очень хорошо заметный на нескольких транспортах. Существующий код занимается переливанием из пустого в порожнее. Сначала упаковывает все коды в Receipt. Затем во frontend'е его if'ами/case'ами распаковывает. Если мы получили UnreacheableException (почему оно, кстати???), то на фронтенде мы можем сразу ответ начать выдавать, вместо заворачиванием этого исключения в красивую обертку. Ну и receipt в текущем виде становится странным. Там появляются хитрые варианты вроде "это вроде бы и receipt, но он не на покупку, а на системную ошибку". Его теперь приходится везде проверять на валидность.

Receipt просто данные, отправляемые юзеру как результат заказа, подразумевается, что этот объект просто рисуется пользователю. Вообще, не важно что там, по крайней мере в этом примере.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[24]: своё vs. сторонее
От: . Великобритания  
Дата: 25.10.13 22:01
Оценка:
Здравствуйте, Ikemefula, Вы писали:

.>>Ну покажи, посмотрим.

.>>DI это один из вариантов реализации IoC для зависимостей. Есть ещё SL, ну и глобальные переменные. Так что ты предлагаешь-то?
I>IoC это обрезание депенденсов, а не управление ими. А вот после обрезания ими можно управлять, хоть через локатор, хоть через DI, хоть через глобальные переменные, а можно и вовсе руками. Потому IoC != DI.
Ещё раз: "DI это один из вариантов реализации IoC для зависимостей". Где я говорю "="?

I>Твой onPost объявлен, внимание, в конкретном модуле, то есть, прибит к нему гвоздями.

Нет, этот сервлет тоже компонент, создаваемый с DI контейнером.

I>Все параметры биндов, внимание, это жосткие зависимости

I>То есть, модуль, гвоздями прибит к депенденсам.
I>Вот уже здесь произошел фейл — смысла в DI в данном примере нет и быть не может. Что вобщем то очевидно, у DI масштаб на порядок побольше, чем один твой onPost.
Правильно. Ибо это "модуль", т.е. декларативное описание куда кого байндить при конфигурировании приложения. В одном месте это будет веб-приложение, где с сервлетом, в другом приложении будут другие модули, собирающие приложение с SmsOrderProcessor и т.п.
Т.е. управление зависимостями происходит в одном месте, в простом декларативном коде.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[17]: своё vs. сторонее
От: IT Россия linq2db.com
Дата: 26.10.13 05:34
Оценка:
Здравствуйте, fddima, Вы писали:

F> PS: Я тут на днях пробовал к linq2db прикрутить csharp-sqlite (полностью мэнэджет/шарповый порт) — заработало блин, без модификации самого linq2db, с добавлением мелкого провайдера на 10 строк. Мега классная штука.


Дык потому что если с DI, который сосёт как трофейный пылесос, то в 10 строк точно было бы не уложиться
... << RSDN@Home 1.2.0 alpha 5 rev. 69>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[16]: своё vs. сторонее
От: Baudolino  
Дата: 28.10.13 12:10
Оценка:
IT>Понятно. Стандартная отмазка. Впрочем, не очень то и хотелось ни твоего кода, ни уж тем более к тебе на работу.
А раз не хотелось, зачем спрашивать? Чего доказывать моим кодом собрался, какие выводы делать? Давай поговорим об этом: сколько килобайт, сколько строк тебе нужно для адекватного суждения? Проект с DI сюда копипастой не выложить — многобуков. Готов code review на Github сделать? Java подойдет?

P.S.Переход на личность оппонента — это один из признаков того, что в споре аргументы по существу закончились.
Re[16]: своё vs. сторонее
От: Baudolino  
Дата: 28.10.13 12:18
Оценка:
IT>Как ты думаешь, что тестирует этот якобы "тест"? Я тебе отвечу. Он тестирует умение компилятора вызывать виртуальный метод.
Это ошибочное утверждение, основанное на неверном прочтении исходного кода. Если одним словом, бред.
В приведенном примере тестируется последовательность из списания денег с карты, записи в лог и формирования корректного ответа. Не тестируются детали реализации каждого из шагов.
Вообще в этом треде все говорит за то, что вы не умеете писать (и читать) тесты, потому что подобные примеры разжеваны практически в каждой книжке по тестированию с объяснением того, зачем это нужно.
RTFM.
Re[17]: своё vs. сторонее
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 29.10.13 11:13
Оценка:
Здравствуйте, Baudolino, Вы писали:

IT>>Как ты думаешь, что тестирует этот якобы "тест"? Я тебе отвечу. Он тестирует умение компилятора вызывать виртуальный метод.

B>Это ошибочное утверждение, основанное на неверном прочтении исходного кода. Если одним словом, бред.
B>В приведенном примере тестируется последовательность из списания денег с карты, записи в лог и формирования корректного ответа. Не тестируются детали реализации каждого из шагов.

Про какой именно пример ты говоришь ? Можно ссылкой ?

B>Вообще в этом треде все говорит за то, что вы не умеете писать (и читать) тесты, потому что подобные примеры разжеваны практически в каждой книжке по тестированию с объяснением того, зачем это нужно.

B>RTFM.

Ты вот сюда посмотри сначала
https://github.com/linq2db
https://github.com/linq2db?tab=members
Re[20]: своё vs. сторонее
От: . Великобритания  
Дата: 29.10.13 12:07
Оценка:
Здравствуйте, fddima, Вы писали:

F> Ребята. Если вы не авторы PaypalCreditCardProcessor — то вам нефиг его тестить.

А ещё мы не авторы софта для АЭС и на Эверест не поднимались, да и дядька в Киеве.

F>Если вы хоть кто-нибудь хоть когда-нибудь сталкивались с разработкой процессинга этих самых платежных карт — то вся ветка кажется смешной.

F>...
F> г) вэлком в реальный мир. DI — нихера не помогает в тестировании.
Ага, правильно. Без таблиц роутинга пиццу продавать нельзя, это закон природы, сразу после E=mc2.

F> PS: Как с эмулировать CardProcessing без DI? Слышал ли кто-нибудь о socket или http listener?

Правильно, твой код особенный, аккуратно продуман, чтобы быть нетестируемым. Ибо тестируемость кода — типичное ламерство. Настоящие Гуру создают Настоящий Код, тестировать Настоящий Код можно только в гамаке и стоя.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[18]: своё vs. сторонее
От: Baudolino  
Дата: 30.10.13 16:13
Оценка:
IT>>>Как ты думаешь, что тестирует этот якобы "тест"? Я тебе отвечу. Он тестирует умение компилятора вызывать виртуальный метод.
B>>Это ошибочное утверждение, основанное на неверном прочтении исходного кода. Если одним словом, бред.
B>>В приведенном примере тестируется последовательность из списания денег с карты, записи в лог и формирования корректного ответа. Не тестируются детали реализации каждого из шагов.
I>Про какой именно пример ты говоришь ? Можно ссылкой ?
https://code.google.com/p/google-guice/wiki/Motivation
Re[19]: своё vs. сторонее
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 30.10.13 16:15
Оценка:
Здравствуйте, Baudolino, Вы писали:

IT>>>>Как ты думаешь, что тестирует этот якобы "тест"? Я тебе отвечу. Он тестирует умение компилятора вызывать виртуальный метод.

B>>>Это ошибочное утверждение, основанное на неверном прочтении исходного кода. Если одним словом, бред.
B>>>В приведенном примере тестируется последовательность из списания денег с карты, записи в лог и формирования корректного ответа. Не тестируются детали реализации каждого из шагов.
I>>Про какой именно пример ты говоришь ? Можно ссылкой ?
B>https://code.google.com/p/google-guice/wiki/Motivation

А, ну, всё в порядке, я спокоен
Re[17]: своё vs. сторонее
От: IT Россия linq2db.com
Дата: 30.10.13 17:38
Оценка:
Здравствуйте, Baudolino, Вы писали:

B>P.S.Переход на личность оппонента — это один из признаков того, что в споре аргументы по существу закончились.


Получается, что у тебя аргументы закончились с твоего самого первого сообщения в этой ветке.
Если нам не помогут, то мы тоже никого не пощадим.
Re[17]: своё vs. сторонее
От: IT Россия linq2db.com
Дата: 30.10.13 17:52
Оценка:
Здравствуйте, Baudolino, Вы писали:

IT>>Как ты думаешь, что тестирует этот якобы "тест"? Я тебе отвечу. Он тестирует умение компилятора вызывать виртуальный метод.

B>Это ошибочное утверждение, основанное на неверном прочтении исходного кода. Если одним словом, бред.

Бред — это этот твой пример и подобные тесты.

B>В приведенном примере тестируется последовательность из списания денег с карты, записи в лог и формирования корректного ответа. Не тестируются детали реализации каждого из шагов.


В этом тесте тестируется умение компилятора вызывать пару фейковских методов и больше ничего. А если перейти от теории к практике, то в реальном проект у тебя будет десятки и сотни подобных методов и детсятки или сотни раз ты будешь тестировать одно и тоже — умение компилятора вызывать моки. Логи и формирование ответов — это часть инфраструктуры проекта и может быть протестирована отдельно один единственный раз.

B>Вообще в этом треде все говорит за то, что вы не умеете писать (и читать) тесты, потому что подобные примеры разжеваны практически в каждой книжке по тестированию с объяснением того, зачем это нужно.


Одно из трёх — либо ты плохо умеешь читать, либо не задумываешься над тем, что прочитал и выхватываешь оттуда только устраивающее тебя, либо ты читаешь какие-то левые книжки. Выброси их на помойку или сдай в макулатуру и больше не позорься. Есть очень хороший критерий качества подачи материала — если автор расписывает только достоинства инструмента, но старательно опускает недостатки, то здесь что-то не так и всегда возникает вопрос — а в чём подвох? Если в твоих книжках не написано какие проблемы несут с собой защищаемые тобой подходы, то это плохие книжки. А если учесть, что в последнее время пошла мода издавать книжки, которые по сути являются тематической компиляцией самых разношёрстных блогов и википедий, то в качестве таких книжек сомневаться не приходится.
Если нам не помогут, то мы тоже никого не пощадим.
Re[21]: своё vs. сторонее
От: fddima  
Дата: 30.10.13 21:06
Оценка:
Здравствуйте, ., Вы писали:

Слушай, точечка — ты либо по делу скажи, либо молчи уже тогда, что-ли, если по делу вставить нечего.
Re[22]: своё vs. сторонее
От: . Великобритания  
Дата: 30.10.13 21:31
Оценка:
Здравствуйте, fddima, Вы писали:

F>Слушай, точечка — ты либо по делу скажи, либо молчи уже тогда, что-ли, если по делу вставить нечего.

На бред отвечаю бредом, уж извини.
Если тебе есть что показать, показывай, пока только пальцекидание
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[23]: своё vs. сторонее
От: fddima  
Дата: 31.10.13 11:27
Оценка:
Здравствуйте, ., Вы писали:

Я лично не писал ничего для АЭС и на эверест не подымался. Поставил бы минус, если не согласен со мной. Если у тебя другой опыт — поделись, тут все за.
Re[23]: своё vs. сторонее
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.13 11:53
Оценка:
Здравствуйте, ., Вы писали:

F>>Слушай, точечка — ты либо по делу скажи, либо молчи уже тогда, что-ли, если по делу вставить нечего.

.>На бред отвечаю бредом, уж извини.
.>Если тебе есть что показать, показывай, пока только пальцекидание

Шота не совсем ясно, чего вы с Baudolino хотите сказать.

Пример который вы дали, мутный — в ём ровно одна функция, очень простая, полезного кода около трех с половиной строчек. Ради этой функции сделано целых несколько классов и куча кода с различными приседаниями.
Мне например, совершенно не ясно, зачем вообще класс RealBillingService и все приседания вокруг него.

Весь метод который вы показали, это обработка ошибки и возврат результата

При этом
1 депенденсы на внутренности Receipt — forSuccessfulCharge, forDeclinedCharge, forSystemFailure
2 депенденси на внутренности ChargeResult — wasSuccessful
3 не ясно, что за процессор такой, если не умеет обрабатывать исключения и не может правильный результат вернуть
4 совершенно не ясно, почему этот процессор не логирует операции и ошибки

То есть, судя по коду, метод сервису тупо ворует обязанности у Receipt, PayPalProcessor и ChargeResult и при этом у него целый вагон депенденсов
Теперь очевидно, что DI ровно ничео не меняет — у метода вагон депенденсов, и ради трех с половиной строчек кода надо нагородить целую инфраструктуру

Внятный дизайн за счет обрезания депенденсов и перераспределения обязанностей можно сделать вот таки

CreditCardProcessor processor = ххх; // единственная "тяжёлая" депенденси

return Receipt.FromOperation(() => processor.charge(creditCard, order.getAmount()));


И вот за счет чего


// Receipt инкапсулирует все что ему надо, раз у него куча интересных методов
static class Receipt 
{

  static Receipt FromOperation(Func<ChargeResult> operation)
  {
    try {
      ChargeResult result = operaiton();
   
      return result.wasSuccessful()
          ? forSuccessfulCharge(result.getChargeAmount()) // не ясно, для чего getAmount тащить через order, если передаем его в операцию  :xz: 
          : forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      return forSystemFailure(e.getMessage());
    }
  }
}
// процессор делает логирование и минимальную обработку ошибок, возвращает внятный результат
class xxxProcessor {
  charge(CreditCard card, Amount amount)
  {
    try{
      var result = ... // internal processing
      
      transactionLog.success(result);
      
      return ToChargeResult(result, amount);
    }
    catch(xxxException ex)
    {
       transactionLog.failure(ex);
       throw; // !!!
    }  
 } 
}


Итого — DI умёр, правда без мучений
Теперь самое страшное — ради эти двух строчек, совершенно очевидно, не надо целый класс вводить, а метод тупо заинлайнить туда, где будет соответсвующй процессор, а Receipt судя по коду, вообще будет везде доступен.

processor.charge и Receipt.FromOperation стоит покрыть тестами, с первым неясно, что унутре, а со вторым все просто — он будет зависеть только от входного параметра, и не надо никаких моков, классов, интерфейсов и прочего говна.
Если логика процессора будет достаточно сложной, понадобятся моки, для изоляции от сети, скажем, или базы данных и тд.
Re[24]: своё vs. сторонее
От: . Великобритания  
Дата: 31.10.13 12:08
Оценка:
Здравствуйте, fddima, Вы писали:

F> Я лично не писал ничего для АЭС и на эверест не подымался. Поставил бы минус, если не согласен со мной. Если у тебя другой опыт — поделись, тут все за.

Так не с чем не соглашаться, и нет у меня цели опытом делиться, кто какой софт пишет, это в соседний форум, "О жизни". Мы тут вроде обсуждаем DI, IoC-контейнеры, я привожу пример для guice, как он помогает автоматизировать DI, в чём помогает DI, а ты отвечаешь что писать процессинг кредиток — тяжело. Кхм.. И?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[24]: своё vs. сторонее
От: . Великобритания  
Дата: 31.10.13 14:47
Оценка:
Здравствуйте, Ikemefula, Вы писали:

F>>>Слушай, точечка — ты либо по делу скажи, либо молчи уже тогда, что-ли, если по делу вставить нечего.

.>>На бред отвечаю бредом, уж извини.
.>>Если тебе есть что показать, показывай, пока только пальцекидание
I>Шота не совсем ясно, чего вы с Baudolino хотите сказать.
Объяснить что такое DI. Судя по ответам для вас это некое абстрактное зло о котором пишут только в проклятых книжках, которое нужно всеми силами избегать, и не поминать всуе.

I>Пример который вы дали, мутный — в ём ровно одна функция, очень простая, полезного кода около трех с половиной строчек. Ради этой функции сделано целых несколько классов и куча кода с различными приседаниями.

I>Мне например, совершенно не ясно, зачем вообще класс RealBillingService и все приседания вокруг него.
Класс в общем-то один (RealBillingService). Остальное — интерфейсы, которые у тебя "уже есть". Скажем, пишутся другой компанией, ты лишь подключаешь либу к себе в проект и юзаешь интерфейс.
Да, там ещё есть интерфейс BillingService, который сделан лишь "для красоты", ибо у некоторых такой стиль программирования, можно его выкинуть. Я об этом уже раз пять писал.

I>Весь метод который вы показали, это обработка ошибки и возврат результата

Это минимальный пример бизнес-логики.
I>При этом
I>1 депенденсы на внутренности Receipt — forSuccessfulCharge, forDeclinedCharge, forSystemFailure
Receipt — это просто структура данных, там никакой логики нет, обычный immutable объект, никаких внутренностей. Методы "for*" это стандартный подход замещения множества конструкторов статическими фабричными методами, чтобы в имени метода выразить назначение конструктора. Примерно как DateTime.forYMD(2013, 10, 31), DateTime.forToday() и т.п.

I>2 депенденси на внутренности ChargeResult — wasSuccessful

Ы?

I>3 не ясно, что за процессор такой, если не умеет обрабатывать исключения и не может правильный результат вернуть

Процессор логгирует обработку платежа. BillingService обрабатывает заказы. Это совершенно разные бизнес-сущности.

I>4 совершенно не ясно, почему этот процессор не логирует операции и ошибки

Логгирует. Но transactionLog это не наша программерская либа для логирования, а лог банковских транзакций из бизнес-требований, который пишется обычно в БД.

I>То есть, судя по коду, метод сервису тупо ворует обязанности у Receipt, PayPalProcessor и ChargeResult и при этом у него целый вагон депенденсов

I>Теперь очевидно, что DI ровно ничео не меняет — у метода вагон депенденсов, и ради трех с половиной строчек кода надо нагородить целую инфраструктуру
Не ворует, а следует SRP.

I>

I>// Receipt инкапсулирует все что ему надо, раз у него куча интересных методов
Ну и зря. Тащим бизнес-логику в класс данных.

I>// процессор делает логирование и минимальную обработку ошибок, возвращает внятный результат
А теперь добавляем BarclaysProcessor, который должен работать в американской пиццерии, т.к. они договорились с банком на меньшую коммисию, и наслаждаемся копипастой.
I>


I>Итого — DI умёр, правда без мучений

Ну-ну, а теперь покажи код как transactionLog попадает внутрь PayPalProcessor?

Теперь новое требование, мелкое изменение. Нужно при оформлении заказа, если заказ прошел успешно, послать уведомление повару приготовить пиццу. В случае с BillingService класс поменяется так:
  private final CookNotificationService cookNotificationService;
  @Inject
  public RealBillingService(CreditCardProcessor processor,
      TransactionLog transactionLog,
      CookNotificationService cookNotificationService) {
    this.processor = processor;
    this.transactionLog = transactionLog;
    this.cookNotificationService = cookNotificationService;
  }
...
  if (result.wasSuccessful())
     cookNotificationService.send(order);

Всё. Ещё, конечно, добавится класс CookNotificationService. Остальной существующий код останется без изменений. Тебе же придётся менять BarclayProcessor и PayPalProcessor и везде где есть "CreditCardProcessor processor = ххх; // единственная "тяжёлая" депенденси"
Кстати, раскрой плз что значит этот "xxx".

I>Теперь самое страшное — ради эти двух строчек, совершенно очевидно, не надо целый класс вводить, а метод тупо заинлайнить туда, где будет соответсвующй процессор, а Receipt судя по коду, вообще будет везде доступен.


I>processor.charge и Receipt.FromOperation стоит покрыть тестами, с первым неясно, что унутре, а со вторым все просто — он будет зависеть только от входного параметра, и не надо никаких моков, классов, интерфейсов и прочего говна.

Receipt вообще покрывать не надо, там максимум конструкторы/геттеры/свойства, логику туда тащить не надо. Он сам покроется при тестировании других классов.

I>Если логика процессора будет достаточно сложной, понадобятся моки, для изоляции от сети, скажем, или базы данных и тд.

Так естественно понадобятся. У тебя есть сомнения?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[25]: своё vs. сторонее
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 31.10.13 16:19
Оценка:
Здравствуйте, ., Вы писали:

I>>Шота не совсем ясно, чего вы с Baudolino хотите сказать.

.>Объяснить что такое DI. Судя по ответам для вас это некое абстрактное зло о котором пишут только в проклятых книжках, которое нужно всеми силами избегать, и не поминать всуе.

В моих ответах есть подробное описание, где и как у меня применяется DI.

I>>Пример который вы дали, мутный — в ём ровно одна функция, очень простая, полезного кода около трех с половиной строчек. Ради этой функции сделано целых несколько классов и куча кода с различными приседаниями.

I>>Мне например, совершенно не ясно, зачем вообще класс RealBillingService и все приседания вокруг него.
.>Класс в общем-то один (RealBillingService). Остальное — интерфейсы, которые у тебя "уже есть". Скажем, пишутся другой компанией, ты лишь подключаешь либу к себе в проект и юзаешь интерфейс.
.>Да, там ещё есть интерфейс BillingService, который сделан лишь "для красоты", ибо у некоторых такой стиль программирования, можно его выкинуть. Я об этом уже раз пять писал.

Как то выходит, что и RealBillingService так же для красоты.

I>>Весь метод который вы показали, это обработка ошибки и возврат результата

.>Это минимальный пример бизнес-логики.
I>>При этом
I>>1 депенденсы на внутренности Receipt — forSuccessfulCharge, forDeclinedCharge, forSystemFailure
.>Receipt — это просто структура данных, там никакой логики нет, обычный immutable объект, никаких внутренностей. Методы "for*" это стандартный подход замещения множества конструкторов статическими фабричными методами, чтобы в имени метода выразить назначение конструктора. Примерно как DateTime.forYMD(2013, 10, 31), DateTime.forToday() и т.п.

Вот-вот. Раз там логики нет, значит обязанности надо будет протаскивать куда попало.

I>>2 депенденси на внутренности ChargeResult — wasSuccessful

.>Ы?

Ога.

I>>3 не ясно, что за процессор такой, если не умеет обрабатывать исключения и не может правильный результат вернуть

.>Процессор логгирует обработку платежа. BillingService обрабатывает заказы. Это совершенно разные бизнес-сущности.

Может и так, но судя по коду, логируется ChargeResult который спокойно может логироваться в процессоре.

I>>То есть, судя по коду, метод сервису тупо ворует обязанности у Receipt, PayPalProcessor и ChargeResult и при этом у него целый вагон депенденсов

I>>Теперь очевидно, что DI ровно ничео не меняет — у метода вагон депенденсов, и ради трех с половиной строчек кода надо нагородить целую инфраструктуру
.>Не ворует, а следует SRP.

Не ясно, где там SRP

I>>
I>>// Receipt инкапсулирует все что ему надо, раз у него куча интересных методов
.>Ну и зря. Тащим бизнес-логику в класс данных.

Эту логику можно вынести в свободные методы, собственно, у меня так и сделано.


I>>// процессор делает логирование и минимальную обработку ошибок, возвращает внятный результат
.>А теперь добавляем BarclaysProcessor, который должен работать в американской пиццерии, т.к. они договорились с банком на меньшую коммисию, и наслаждаемся копипастой.

Покажи код. Мне совсем неочевидно, почему там должна копипаста появиться. 

I>>


I>>Итого — DI умёр, правда без мучений

.>Ну-ну, а теперь покажи код как transactionLog попадает внутрь PayPalProcessor?

Это уже не важно. Я показал что приведеный пример никому не интересен

.>Теперь новое требование, мелкое изменение. Нужно при оформлении заказа, если заказ прошел успешно, послать уведомление повару приготовить пиццу. В случае с BillingService класс поменяется так:

.>Всё. Ещё, конечно, добавится класс CookNotificationService. Остальной существующий код останется без изменений. Тебе же придётся менять BarclayProcessor и PayPalProcessor и везде где есть "[i]CreditCardProcessor processor = ххх;

Смотри внимательно — в исходном примере используется CreditCardProcessor. Если его легко заменить на BarclayProcessor , то не ясно, почему это нельзя сделать в моём случае .

Покажи код, как ты собираешься всунуть нотификацию, а я покажу свой вариант. Идет ?

.>Кстати, раскрой плз что значит этот "xxx".


В вызывающем коде гарантировано есть нужная депенденси, её напрямую можно юзать

I>>processor.charge и Receipt.FromOperation стоит покрыть тестами, с первым неясно, что унутре, а со вторым все просто — он будет зависеть только от входного параметра, и не надо никаких моков, классов, интерфейсов и прочего говна.

.>Receipt вообще покрывать не надо, там максимум конструкторы/геттеры/свойства, логику туда тащить не надо. Он сам покроется при тестировании других классов.

Receipt != Receipt.FromOperation

I>>Если логика процессора будет достаточно сложной, понадобятся моки, для изоляции от сети, скажем, или базы данных и тд.

.>Так естественно понадобятся. У тебя есть сомнения?

Вот и покажи внятный пример, а не этот фейк что по ссылке
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.