Pull to refresh

Comments 30

Вот ещё бы условия записывались не простыми строками…
в jvm-языке Scala есть конструкция require( a == b), которая определена так:

def require(requirement: Boolean, message: => Any) {
if (!requirement)
throw new IllegalArgumentException("requirement failed: "+ message)
}


в целом, очень удобно =) вы можете сделать так же и в джаве (ProGuard потом сделает inline, конечно же), и не тянуть за проектом килобайты странных библиотек.
> В отличие от assert’ов, проверки выполняются в runtime

На всякий случай напомню, что assert в Java тоже выполняется исключительно в runtime. Правда, нужно указывать флаг -ea (enable assertions).

Так что на мой взгляд — единственная разница межу assert и @Requires в том, что @Requires не нужно выискивать в коде метода.
Еще assert'ы не рекомендуется использовать в public-методах.
Так что на мой взгляд — единственная разница межу assert и @Requires в том, что @Requires не нужно выискивать в коде метода.

Технически — всё верно.
Принципеальная разница в том, что ассерты являются частью реализации, а контракты это часть интерфейса. Поэтому, по идее, можно придумать способ для их статической верификации.
Тут нет разницы. Сейчас эта «часть интерфейса» анализируется чёрти-как с помощью фреймворка.
Если они делают хоть какую-то статическую верификацию, значит вынуждены читать байт-код/исходники методов. А следовательно, и могут с тем же успехом разобрать assert/require. Даже проще будет.

Если не делают — это вообще не даёт никаких преимуществ вроде декларативности, лишь сплошные недостатки.

Мой вывод — весь данный framework заменяется либо assert либо вызовом метода из одной строчки.
Никакого колхоза.
В геттере проверка не нужна, а в сеттере надо кидать IllegalArgumentException, тогда её назначение сразу очевидно.
Я парвильно понимаю, что суть DbC — это избавить код от всевозможных проверок и писать так, будто все объекты возращяют допустимые значения и на вход всех методов поступают допустимы значения?
не совсем.
«Программирование по контракту» подразумивает, что разработчики между собой «договариваются», о некоторых ограничениях. Это позволяет проверки, коих тысячи, вынести «на переферию» + на этапе тестирования контролировать — не забыл ли кто об этих соглашениях.

Например есть у вас вебприложение которое, якобы, построено в соответсвии с модным ныне buzz-word MVC.
У вас есть контроллер, который который подготавливает данные пользователя, передаёт эти данные Модели (сервису) в качесвте аргумента/тов. Внутри Модели происходят некоторые действия в соответсвии с бизнесс-логикой, результаты которых «обычно» сохранянются (DAO/Repository).
Так вот — в этой схеме использование «программирования по контракту» и автосредств проверки соглашений позволит не писать в каждом слое тривиальные проверки вроде «Фамилия != null», а вынести их все например в контроллер а дальше считать, что если данные переданы — то они соответсвуют соглашениям. Соответсвенно если есть этап тестирования и кто-то где-то забыл проверить данные и передаёт обработку дальше данные анрушающие контракт — это всё тут же проявится. Причём в том месте где произошёл нарушающий контракт вызов, а не как обычно — на 20-30 уровней глубже гдето выпадает NullPointerException.

Всё это имеет смысл только в разработке. В продакшене толку от контрактов немного — если в системе появились данные нарушающие контракт — возможность восттановления после такой ошибки вызывает сильные сомнения.

Другой вариант — описывать с помощью контрактов бизнесс-ограничения. Тогда ограничения становятся одной из составляющих кода (должны быть и в продакшене). В этом случае использование аннотаций имеет смысл только если есть возможность автоматически генерировать их например из выражений OCL на UML диаграмме (я не исключаю такой возможности, но незнаю/не слышал о таком). Если же автогенерации нет — то мы ничего не выгрываем по сравнению с написанием кода методов выполняющего те же проверки.
Спасибо за развернутый ответ. Впринципе я так примерно и понимал. Даже использовал частично, сам того не зная :) У меня в коде натыкано Preconditions.checkArgument/checkNotNull (это из Google Collections)
Я так понял, что за длинным постом имелось ввиду то, что пос соглашению разрботчиков, проверка условий (контракт) выносятся на высший уровень (в данном случае на уровень контроллера)?

А что значит, что используется только в разработке. Когда дело доходит до продакшена — все нужно выдергивать и обрабатывать уже жестко?
Дисклеймер:
1)я не видел ни одного серьезного проекта где было бы использована разработка по контракту.
2)мне неизвестно об исследованиях в которых бы говорилось, что «мы внедрили „контракты“ и количество ошибок выходящих в продакшен уменьшилось на столько-то (много), а время разрботки увеличилось на мало/уменьшилось». Если у кого-то есть такая информация — это очень интересно.
Всё написанное лишь мысли на заданную тему, опирающиеся на прочитанные мной статьи на эту тему.

насколько я понимаю контракт класса помогает обнаруживать, что кто-то из клиентских классов его не соблюдает, при этом не кодируя бесконечные одинаковые if. Очевидно что такое знание полезно на этапе разработки, так как облегчает локализацию нарушения контракта и практически бесполезно в продакшене: если один из компонентов нарушает контракт, значит он работает неправильно. Даже если выбросить исключение — возможность восстановления после него выглядит сомнительной и бессмысленной. Поэтому, в частности, assert'ы в Java можно включать и выключать.
Так же — теоретически может существовать возможность автоматической трансляции ограничений записанных например на языке OCL (причём как прмитивных вроде что фамилия человека не может быть null, так и высокоуровневых бизнес-ограничений вроде того, что гражданина нельзя призвать в армию, если он проходил службу в вооруженных силах иностранного государства) в моделях UML.

Но есть во всей это теории скользкий момент.
Если контракт dev-time only то что делать когда в продакшен утечет компонент нарушающий контракт — очевидно что приведёт к нарушению согласованности/целостности данных. Значит всё-равно приходится кодировать проверки самому. Лишний труд + Дублирование знаний.Ничего не выиграли.
Если контракт и production-time, то возникает другой вопрос — чем он лучше чем запись условий и инвариантов в коде? Да иногда бывает меньше символов, но у записи контрактов в виде разного рода аннотаций есть тот недостаток что все параметры-строки. Значит нет автокомплита, нет поддержки рефаткоринга. IDE ==> блокнот. Так что тоже — сомнительный выигрыш.

Чашу весов в сторону контрактов могла бы склонить автогенерация и какая-то автообработка — но как я писал — я о таком не слышал.

Возможно поэтому программирование по контракту, появившись ещё во времена оны пока не стало популярной практикой.

Да, верно.

Тип функции с пред- и постусловиями можно читать так:

«функция гарантирует, что вернет что-то удовлетворяющее пост-условию, если на входе будет что-то, удовлетворяющее пред-условию»

(читать, конечно, надо в техническом смысле, а не в общечеловеческом; получается что-то вроде forall x:A. P(x) -> B(x), где x — входной параметр, P — пред-условие, B — пост-условие)
Как уже заметили выше, колхоз — это проверки в геттере.
И большой вопрос, насколько нужно выполнять такие проверки в рантайме и оставлять их в релизе. Для классов вроде Time вполне будет достаточно обычных юнит-тестов.
Вроде же уже было на хабре?

Штука интересная, попробую как-нибудь использовать для простой валидации.

Но видно несколько недостатков:

— можно проверять только простые условия. К слову сказать — в дне БЫВАЕТ 25 часов (дни перевода часов). Как в таком случае поменять аннотацию? Или забить на библиотеку и использовать паттерн «колхоз»?

— названия переменных в строках — привет рефакторингу (А так же Гослингу за наше счастливое детство, что нельзя сделать @Requires ({Time.class.intHOURS >= 0,Time.class.intHOURS < = 23}))

— ThrowEnsures — я так понимаю исключения выглядят как «метод setHours был вызван с параметром 50, но условие такое-то не выполняется». уже неплохо, но если хочется добавить больше контекста («минуты, секунды = столько-то» — возвращаемся к колхозу.

Вообще тут пример со временем не очень. То, что в дне бывает 25 часов и 24 — это информация о домене, мне кажется тут лучше просто закодить, а не использовать DbC.
> Используем готовое, изящное решение.
> @Requires ({“newHOURS>= 0”,“newHOURS<= 23” })

Какое же оно изящное, когда все в литералах? Никакой подстветки и CodeComplete…
А как оно сказывается на производительсноти? Я правильно понимаю, что когда нужно, можно всё это дело отключить просто убрав javaagent в аргументах vm и как будто там его и не было?
Не вижу преимуществ перед assert-ами. Чем плохи контракты:
1. Требует конфигурирования дополнительных библиотек и средств компиляции.
2. Для выражения условий контрактов отсутвует поддержка codeassist в IDE, что делает написание оных неприятным.
3. Обычный javadoc с @param понятнее и нагляднее, нежели @Required, к тому же вылезает тултипом в любой IDE в codeassist. По сути javadoc — это и есть контракт.
UFO just landed and posted this here
Претимущество перед ассертами появляется, если эти аннотации можно использовать на методах интерфейса или абстрактных методах. Тогда их не нужно повторять для каждой реализатии.
Кстати, как используются эти аннотации в наследуюмых классах? Можно ли их переопределять, дополнять?
UFO just landed and posted this here
Сорри за некропостинг, но есть ли способ прицепить это к мавену?
На трекере есть соответствующий issue, там подробности. Но кое-какая поддержка есть.
Спасибо за ответ.
Даже не ожидал, что вы ответите )).
вылезло в «прямом эфире», подумал, что что-то новое. кофоджу так на проект и не прикрутил :-(
Я подумываю наши самодельные аннотации такого рода попробовать заменить на эти.
Тогда у нас на совести останется только построение интерфейсов.
судя по молчанию в треде про поддержку мавена, желающие прикручивают самостоятельно. Нафига было делать только под ант — непонятно. С другой стороны, никто не мешает из мавена вызывать антовские таски.
Они в треде что-то пишут про порядок сборки, что я не до конца понял. Типа анту надо сначала запаковать джарник, потом провести тесты, а мавен так делать не позволяет.

Видимо оттуда невозможность использовать ант плагин.
Sign up to leave a comment.

Articles