Должен ли Java POJO проверять поле и исключать исключения в методах setter?
Скажем, у нас есть десятки java POJO, которые представляют мой домен, то есть мои данные в системе, которые текут как объекты между разными слоями моей системы. Системой может быть веб-приложение или простое настольное приложение. То, из чего состоит домен, не имеет большого значения.
При разработке моей системы я запутался, где я должен поместить любую логику проверки. Мои POJO (объекты домена) представляют мои данные, а некоторые из полей внутри этих объектов должны придерживаться определенных критериев, но если я положу много логики проверки внутри своих методов setter, тогда единственный способ рассказать вызывающему клиенту - выбросить исключение. И если я не хочу, чтобы система выходила из строя, исключение должно быть проверенным исключением, которое необходимо поймать и обработать. Следствием этого является то, что каждый раз, когда я создаю новый объект с помощью setter-методов (или даже конструкторов), я делаю либо повторное бросить это исключение, либо использовать блок try-catch. Не кажется правильным заставить вас использовать try-catch для многих методов setter.
Итак, вопрос в том, где я должен поместить свою логику проверки, чтобы я не загромождал свой код с большим количеством шаблонов try-catch и rethrows. Лучшие участники JAVA-байтов могут присоединиться к обсуждению.
Я исследовал и искал Google, но не нашел конкретных обсуждений по этой теме, поэтому я с большим энтузиазмом жду, чтобы получить более глубокое представление о том, как все должно быть сделано.
Ответы
Ответ 1
Возможно, вы ответили на свой вопрос, когда сказали
некоторые из полей внутри этих объектов должны придерживаться определенных критериев
Это всегда помогает думать об инвариантах в вашей системе, то есть о вещах, которые вы хотите поддерживать любой ценой, или о правилах, которые всегда должны соблюдаться.
Ваши POJO являются "последней строкой защиты" для таких инвариантов на ваших объектах данных и, следовательно, подходящим или даже необходимым местом для размещения логики проверки. Без такой проверки объект может уже не представлять что-то, что имеет смысл в вашем домене.
Эти системные инварианты образуют контракт между вашими объектами (или методами) и их "клиентами". Если кто-то пытается использовать их вопреки (надеюсь, хорошо документированному) контракту, бросать исключение - это правильно, так как ответственность за использование отдельных частей системы является ответственностью клиента.
С течением времени я начал одобрять исключенные исключения из-за проверенных для любых случаев нарушения контракта, частично из-за причины, о которой вы упоминаете, а именно, чтобы избежать блоков try
- catch
везде.
Исключенные стандартными исключениями Java включают:
- NullPointerException
- IllegalArgumentException
- IllegalStateException
- UnsupportedOperationException
Лучшей практикой является использование проверенных исключений, если ошибка считается восстанавливаемой и исключенными исключениями в противном случае.
Глава 9 Джошуа Блоха " Эффективная Java, 2-е изд." предоставляет больше мудрости по этой теме:
- Пункт 57: Использовать исключения только для исключительных условий.
- Пункт 58: Использовать проверенные исключения для восстанавливаемых условий и исключений во время выполнения для ошибок программирования.
- Пункт 59: Избегайте ненужного использования проверенных исключений.
- Пункт 60: Использовать стандартные исключения
Ни одно из вышеперечисленных не должно препятствовать вам использовать любую подходящую логику проверки на более высоких уровнях, особенно для обеспечения соблюдения каких-либо конкретных бизнес-правил или ограничений в контексте.
Ответ 2
В целом я также не думаю, что там есть уникальное решение, соответствующее каждой потребности, и оно просто сводится к вашему сценарию и предпочтениям.
С точки зрения инкапсуляции, я считаю, что проверка сеттера - это правильный путь, поскольку логическое место, чтобы решить, является ли предоставленная информация правильной и предлагает подробное объяснение того, что может быть неправильным. Однако я не уверен, что вы подразумеваете под этим:
И если я не хочу, чтобы система выходила из строя, исключение должно быть проверенным исключением...
Почему система выйдет из строя? Непроверенные исключения могут быть очень хорошо пойманы, как проверенные. Вам нужно выяснить, как ваша программа должна вести себя, когда такое событие происходит, поэтому вы можете решить, где их поймать и что делать.
Проверено и не проверено давно, и все еще обсуждается различными способами и убеждениями, но я не вижу причин, по которым вы не должны бросать исключение. Просто сделайте общий ConfigurationException
(или используйте уже существующий IllegalArgumentException
) или что-то, что плавает на вашей лодке, соответственно отметьте сигнатуры вашего метода и добавьте соответствующие java-документы, чтобы любой, кто их называет, знал, чего ожидать, и бросайте его, когда требуется.
В зависимости от ваших объектных отношений и иерархии другим решением могут быть некоторые сборщики, которые запускают пользовательские проверки только при создании экземпляров. Но, как я уже сказал, это действительно зависит от сценария, и вы не сможете помешать другим людям создавать экземпляр и некорректно заполнять некоторые объекты.
Ответ 3
В некоторых ситуациях решение состоит в том, чтобы сделать все сеттеры частными и предоставить единую точку ввода для инициализации объектов. Тогда все проверки и обработка исключений находятся в этом методе инициализации.
Это также гарантирует, что ни один объект не может быть частично инициализирован.
Изменение состояния объекта также можно обрабатывать таким образом, делая объект неизменным, кроме как через конструктор/метод инициализации, вы можете стать расточительным, если будете злоупотреблять.
Ответ 4
В то время как вы можете поместить логику проверки на методы bean setter, я обнаружил, что на практике более четкое разделение проблем будет заключаться в переносе любой сложной проверки на другой класс, предназначенный для проверки. Услышьте меня.
Использование bean валидации в порядке, но во многих случаях (к которым я уверен, что вы сейчас сталкиваетесь) простая аннотация не проведет достаточно тщательную проверку. Для таких фреймворков, как Spring, Validator
вы можете реализовать это.
Существует множество преимуществ для разделения логики проверки:
- Повторное использование кода. В некоторых случаях проверка сеттера для одного bean может
быть идентичным таковому другого bean. Это также переносится на более сжатые модульные тесты.
- Во многих случаях вы не будете иметь дело с исключениями, но
скорее отображая сообщение об ошибке пользователю
- Вы можете вести журнал надлежащим образом, не заполняя POJO заявлениями о регистрации или не принуждая их внедрять регистратор
- Код гораздо читабельнее, а намерение лучше понято.
Надеюсь, что это поможет.