Лучшая практика для вложенных объектов

Какова лучшая практика для ссылки на вложенные объекты?

Скажи, что у меня есть следующее:

class Outer {
 private InnerA innerA;
 //getters and setters
}

class InnerA {
  private InnerB innerB;
  //getters and setters
}

class InnerB {
  private String someString;
  //getters and setters
}

и в моем контроллере или классе обслуживания мне нужно проверить переменную someString класса InnerB, чтобы убедиться, что она не нулевая или не пустая, поэтому я делаю это:

if (getOuter().getInnerA().getInnerB().getSomeString() != null && !getOuter().getInnerA().getInnerB().getSomeString().equalsIgnoreCase("") {
  //do something
}

Для меня это выглядит грязно и может иметь проблемы, если сами вложенные объекты равны нулю.

Создаю ли я методы getters и setter в родительских объектах для дочерних объектов, проверяющих NULL? Просто интересно, какова была лучшая практика, если таковая имеется и / или что некоторые из вас делают в своем коде?

11 ответов

Решение

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

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

Вы можете использовать Apache Commons BeanUtils для навигации по вашим вложенным свойствам, например так:

Добавить метод getSomeString() к вашему внешнему классу и напишите что-то вроде

PropertyUtils.getNestedProperty(this, "innerA.innerB.someString");

Я не могу вспомнить, если это PropertyUtils класс проверяет нулевые свойства, но я бы посмотрел сайт Apache Commons BeanUtils.

Надеюсь это поможет!

Я бы порекомендовал прочитать Закон Деметры.

У вас есть два варианта:

  1. Использовать шаблон проектирования Null Object
  2. Подождите, пока Java 7 Java 8 нулевой безопасный оператор.

В Java 8+ это можно сделать с помощьюOptional.

      String value = Optional.ofNullable(outer).map(x -> x.getInnerA())
                      .map(x -> x.getInnerB()).map(x -> x.getSomeString())
                      .orElse(DEFAULT_VALUE);

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

На заметку, если вы хотите, чтобы код для длинной строки вызовов был "менее уродливым", я предлагаю проголосовать за добавление оператора Элвиса для монеты проекта в следующей версии Java (хотелось бы, чтобы оно было разделено на 7:-().

Я не думаю, что пользователи Outer должны знать Outer.InnerA.InnerB.SomeString - он похоронен глубоко. Вы не можете изменить реализацию InnerB, не трогая клиентов с разделенными внешними уровнями 3 - так какой смысл даже иметь внутренние классы? Ситуации, которые вы описываете, ужасны и не должны возникать.

Я бы порекомендовал вам сначала рассмотреть, принадлежит ли SomeString к InnerB или InnerA или Outer.

Теперь предположим, что ваша иерархия верна, но SomeString обладает этим уникальным свойством, которое требуется клиентам Outer (если SomeString не уникален в этом смысле, иерархия определенно неверна). В этом случае Outer.getSomeString () или, что еще лучше, Outer.isSomeStringNullOrEmpty (), так что по крайней мере клиенты Outer не должны знать о InnerA и InnerB

PS. someString.equalsIgnoreCase ("") стоит дорого, не используйте это. Гораздо дешевле - someString.length() == 0

Это ограничение Java. Вы должны реализовать вспомогательные методы в родительском "OuterObject", если это поможет вам уменьшить дублирование кода.

Эти вспомогательные методы полезны для объекта, который агрегирует другой объект, и вам нужно проверять, только существует ли вложенное значение.

Код:

getOuter().hasInnerB();

Который сделал бы все нулевые проверки.

Эта проблема часто возникает с объектами, сгенерированными из *.xsd. В сложной структуре XML часто бывает много вложенных дополнительных узлов. И что обычно интересно, так это последний узел. Тогда лучше написать вспомогательные методы, которые ответят на вопросы, если узел существует для повторного использования.

Если дело доходит до вашего образца трески, я обычно пишу что-то подобное

if (hasSomeString(getOuter())) {
  //do something
}

Это грязно, но если бы тебе пришлось сделать это только в одном месте, я бы с этим смирился. В противном случае, я бы реализовал Outer.getSomeString() он скрывает внутренний путь, поскольку ваш внешний класс - это тот, который вы выставляете в качестве интерфейса.

Это также позволяет вам иметь дело со случаем, когда один из промежуточных внутренних классов является нулевым, без необходимости выполнять ряд последовательных проверок каждый раз, когда вы пытаетесь получить доступ someString,

Я написал метод Java 8:

public class Helper {
    public static <IN, OUT> OUT returnNullOrCallFunction(IN o, Function<IN, OUT> f) {
        return o == null ? null : f.apply(o);
    }
}

Теперь вы можете позвонить:

Helper.returnNullOrCallFunction(
        myObject.getSomeOtherObject(),
        SomeOtherObject::toString
);

Если myObject.getSomeOtherObject() является null метод вернется nullиначе позвонит myObject.getSomeOtherObject().toString(),

Это довольно полезно, если вам просто нужно пройти на один уровень глубже.

Для многоуровневых это выглядит ужасно:

Helper.returnNullOrCallFunction(
        Helper.returnNullOrCallFunction(
                myObject.getSomeOtherObject(),
                SomeOtherObject::getAnotherObject
        ),
        AnotherObject::toString
);

Если вы уже используете Spring, рассмотрите BeanWrapperили же ConfigurablePropertyAccessor:

      BeanWrapper propertyAccessor = PropertyAccessorFactory.forBeanPropertyAccess(outer);
String someString = propertyAccessor.getPropertyValue("innerA.innerB.someString");

getPropertyValueвыдает исключение, если какое-либо из вложенных свойств null. вы можете поймать это исключение и вернуть все, что хотите.

Другие вопросы по тегам