Комментарии 24
DeliveryCostOrderHistoryItemPropertyEqualityStrategy
TotalPriceOrderHistoryItemPropertyEqualityStrategy
Spring нервно курит в стороне смотря на такие названия :)
+3
А почему просто не реализовать эквивалентность для записей, которые надо сравнить?
0
Решение в лоб: давайте напишем код, который при поступлении нового изменения заказа будет среди уже существующих изменений того же заказа искать точно такое же
Вот это разве не то, что вы предлагаете? IsEquivalent — метод класса OrderHistoryItem, то есть сделано, как вы и предлагаете: реализована эквивалентность записей, которые нужно сравнивать. Проблема в поддержке этой эквивалентности.
0
- Почему нельзя переопределить стандартный Equals а надо использовать IsEquivalent
- Зачем надо вводить новый слой косвенности на стратегиях, если все равно все сводится к тестированию через reflection? Можно было бы просто либо сравнивать через reflection либо просто в тесте перебирать свойства по очереди и их менять и проверять сто при разичии хотя бы в любом одном свойстве IsEquivalent возвращает true.
0
1. Потому что Equals используется нашим ORM (linq to sql) для определения, одна и та же сущность перед ним или нет. Переопределение Equals кодом, который не соответствует сравнению PK сущностей приведет к тому, что ORM перестанет работать :(
2. Не каждое свойство одинаково участвует в определении эквивалентности. Некоторые свойства не сравниваются вообще (например, даты). Некоторые свойства сравниваются сложным образом, как линии. В простом случае можно сгенерировать код при старте приложения. Но это делает код сложнее, например для дебага. Код, который написан в статье, можно дебажить, рефлексия используется только для проверки.
2. Не каждое свойство одинаково участвует в определении эквивалентности. Некоторые свойства не сравниваются вообще (например, даты). Некоторые свойства сравниваются сложным образом, как линии. В простом случае можно сгенерировать код при старте приложения. Но это делает код сложнее, например для дебага. Код, который написан в статье, можно дебажить, рефлексия используется только для проверки.
0
Если рефлексию использовать для проверки, то можно не гененировать "стратегии" а тестировать только IsEquivalent. Подмножество свойств, которые надо сравнивать можно задать либо атрибутами либо выделить в отдельный класс (OrderHeader, OrderState, OrderVersion)
0
Это тоже решение. Если атрибута не повешено, то можно падать при старте приложения, например. Вполне валидное решение. Проблема может быть только в том, что атрибутов может быть много и код будет сложно читать. Я не очень люблю аспекты, но это уже вопрос вкуса.
0
1. Несколько не понятно — если с точки зрения бизнеса или моделируемого вами бизнес-процесса это одна и та же запись (вы же в корне для этого добавляли isEquivalent) — то почему бы ORM считать это разными записями?
2. Вам надо хранить свойства что не участвуют в вашей бизнес модели? Нет? — чего они тогда не участвуют в эквивалентности. Сравнение сложным образом свойства — точно так же только Equals уже на свойстве вызывается.
2. Вам надо хранить свойства что не участвуют в вашей бизнес модели? Нет? — чего они тогда не участвуют в эквивалентности. Сравнение сложным образом свойства — точно так же только Equals уже на свойстве вызывается.
0
Ещё как варинт можно реализовать тест, который через метаданные проверит, что у класса для каждого свойства есть метод Is{property}Equivalent.
И так же реализовать метод IsEquivalent, который все эти методы будет дёргать.
Понимаю, что рефлексию как-то не принято использовать, но потом будет проще поддерживать этот зоопарк.
Ну и вроде бы о высокой производительности тут речи не было.
И ещё IsEquivalent можно будет научить ругаться, если всё же кто-то не запуская тестов выложил сборку на продакшн.
И так же реализовать метод IsEquivalent, который все эти методы будет дёргать.
Понимаю, что рефлексию как-то не принято использовать, но потом будет проще поддерживать этот зоопарк.
Ну и вроде бы о высокой производительности тут речи не было.
И ещё IsEquivalent можно будет научить ругаться, если всё же кто-то не запуская тестов выложил сборку на продакшн.
0
1. Неймконвеншн будет не статически проверяться, мне, если честно, не очень нравится
2. Можно ошибиться в том, что не вызвать такой метод в IsEquivalent
3. В чём именно проще поддержка? В предложенном варианте со стратегиями именно поддержка проще, потому что нужно просто реализовать интерфейс.
4. Научить IsEquivalent ругаться можно, но лучше делать это не в рантайме, а при инициализации приложения. Тогда и с производительностью не будет проблем.
2. Можно ошибиться в том, что не вызвать такой метод в IsEquivalent
3. В чём именно проще поддержка? В предложенном варианте со стратегиями именно поддержка проще, потому что нужно просто реализовать интерфейс.
4. Научить IsEquivalent ругаться можно, но лучше делать это не в рантайме, а при инициализации приложения. Тогда и с производительностью не будет проблем.
0
1. А наличие интерфейсов для всех свойств статически проверяется разве? Тоже только при запуске. Или вы о чём?
Про не нравится бессмыслено что-либо писать, вкус и цвет…
2. Как ошибиться? Кто-то намеренно скроет свойство от рефлексии?
3. В том, что не надо копипастить интерфейсы и добавлять стратегии в массив. Поди вспомни через Н недель где там этот интерфейс найти.
И не надо прописывать строкой название свойства… это утомительно :)
4. С производительностью не будет проблем только если вы сгенерируете динамически код IsEquivalent, который рефлексию не будет трогать (динамически сгенерируете всё то, что вы предложили с интерфейсами).
5. А ещё кодогенерацию можно использовать, которая все эти интерфейсы по IOrderHistoryItem сгенерирует. А методы Is{property}Equivalent будут выкидывать исключения, пока не будту вручную реализованы. Тут вообще «откиньте спинки ваших кресел».
А ещё в сообщении исключения об отсутствии Is{property}Equivalent разработчику можно было бы давать шаблон для релазиции в виде текста. Скопировал его и реализовал, даже руками название метода писать не надо.
Про не нравится бессмыслено что-либо писать, вкус и цвет…
2. Как ошибиться? Кто-то намеренно скроет свойство от рефлексии?
3. В том, что не надо копипастить интерфейсы и добавлять стратегии в массив. Поди вспомни через Н недель где там этот интерфейс найти.
И не надо прописывать строкой название свойства… это утомительно :)
4. С производительностью не будет проблем только если вы сгенерируете динамически код IsEquivalent, который рефлексию не будет трогать (динамически сгенерируете всё то, что вы предложили с интерфейсами).
5. А ещё кодогенерацию можно использовать, которая все эти интерфейсы по IOrderHistoryItem сгенерирует. А методы Is{property}Equivalent будут выкидывать исключения, пока не будту вручную реализованы. Тут вообще «откиньте спинки ваших кресел».
А ещё в сообщении исключения об отсутствии Is{property}Equivalent разработчику можно было бы давать шаблон для релазиции в виде текста. Скопировал его и реализовал, даже руками название метода писать не надо.
0
НЛО прилетело и опубликовало эту надпись здесь
Насколько я понял, одной из проблем было решение индепотентности операций. С этим намного легче бороться, если добавить при отправке данных уникальный идентификатор команды { OperationID: GUID, Data: any }
, а при повторном принятии команды при RETRY очень легко проверять на дубликаты.
0
По поводу проблемы с IsEquivalent — я бы предложил решение попроще, без необходимости создавать классы стратегий. Достаточно юнит теста на рефлекшене такого вида:
Таким образом, добавляя новое поле, но забыв явно добавить его в ignoredByEquivalent теста, получим ошибку при тестировании.
- в тесте имеем список имен полей не участвующих в проверках внутри IsEquivalent (ignoredByEquivalent)
- пробегаемся рефлекшеном по списку полей. Для каждого поля:
- если нет в списке ignoredByEquivalent, то создаем 2 одинаковых объекта для проверки, меняем в одном из них текущее поле и сравниваем при помощи IsEquivalent, если вернуло true — ошибка, т.к. данное поле должно участвовать в проверке и должно было вернуть fasle,
ведь поле именилось
Таким образом, добавляя новое поле, но забыв явно добавить его в ignoredByEquivalent теста, получим ошибку при тестировании.
0
Стратегии могут быть нетривиальные. Например, время с лагом менее 5 минут считается одинаковым.
На мой взгляд, явные стратегии в боевом коде читаются и тестируются легче, чем эвристический тест, выставляющий разные значения полей.
На мой взгляд, явные стратегии в боевом коде читаются и тестируются легче, чем эвристический тест, выставляющий разные значения полей.
0
Например, время с лагом менее 5 минут считается одинаковым.
Ну не знаю, вроде это правило в одну строчку записывается. Наворачивать вокруг целый класс — как-то слишком, метода было бы достаточно. Если бы там реально сложная логика, на десятки / сотни строк, тогда может быть стратегии действительно были бы оправданы.
Этот тест только проверяет, что не забыли подумать про новое свойство, как раз та изначальная проблема которую вы описали, и не заставляет вводить новые сущности — а значит больше соответствует принципу KISS.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Одних тестов недостаточно, нужна хорошая архитектура