【问题标题】:Nested Objects Best Practice嵌套对象最佳实践
【发布时间】:2011-07-12 01:05:32
【问题描述】:

引用嵌套对象的最佳做法是什么?

假设我有以下内容:

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

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

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

在我的控制器或服务类中,我需要检查 InnerB 类的 someString String 变量以确保它不为空或不为空,所以我这样做:

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

在我看来,这看起来很混乱,如果嵌套对象本身为空,可能会出现问题。

我是否在父对象中为检查 null 的子对象创建 getter 和 setter?只是想知道最佳实践是什么和/或你们中的一些人在代码中做了什么?

【问题讨论】:

    标签: java object nested reference


    【解决方案1】:

    如果您已经在使用 Spring,请考虑 BeanWrapperConfigurablePropertyAccessor

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

    如果任何嵌套属性为nullgetPropertyValue 将引发异常。您可以捕获该异常并返回您想要的任何内容。

    【讨论】:

      【解决方案2】:

      在 Java 8+ 中,这可以通过使用 Optional 来处理。

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

      【讨论】:

      • 自 Java SE 8(2014 年 3 月)发布以来,这是处理此类情况的推荐方式。
      【解决方案3】:

      这是一个 java 限制。 如果它可以帮助您减少代码重复,您应该在父“OuterObject”中实现辅助方法。

      此辅助方法对于聚合其他对象的对象很有用,您只需检查嵌套值是否存在。

      代码:

      getOuter().hasInnerB();
      

      这会做所有的空检查。

      从 *.xsd 生成的对象经常会出现此问题。 在复杂的 xml 结构中,经常会出现许多嵌套的可选节点。通常有趣的是最后一个节点。 然后最好编写帮助方法,如果节点存在以供重用,它将回答问题。

      如果涉及到你的鳕鱼样本,我通常会写类似的东西

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

      【讨论】:

        【解决方案4】:

        我写了一个 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
        );
        

        【讨论】:

          【解决方案5】:

          我建议阅读Law of Demeter

          【讨论】:

          • 我建议您熟悉映射到 POJO 的 XML 文档的美妙世界。只是一个 LoD 根本不适用的例子。
          【解决方案6】:

          您可以使用 Apache Commons BeanUtils 来浏览您的嵌套属性,如下所示:

          将方法 getSomeString() 添加到您的 Outer 类并编写类似

          的内容

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

          我不记得PropertyUtils 类是否检查空属性,但我会查看Apache Commons BeanUtils 站点。

          希望这会有所帮助!

          【讨论】:

            【解决方案7】:

            我的信念是,您不应该通过外部类的方法公开“内部-内部”成员,除非您向它们添加某种功能或不同的行为,或者该值对于外部类的使用是“必不可少的” .不过,这也是一个判断问题,可能会因代码的用途和架构而异。

            附带说明,如果您希望一长串调用的代码“不那么难看”,我建议投票赞成在下一版本的 Java 中为项目硬币添加 Elvis Operator(我希望它成功了)进入 7 :-()。

            【讨论】:

              【解决方案8】:

              你有两个选择:

              1. 使用Null Object design pattern
              2. 等待 Java 7 Java 8 null 安全运算符。

              【讨论】:

                【解决方案9】:

                如果这些对象中的任何一个可以为 null,那么您当然必须在调用该对象的 getter 之前检查是否为 null。

                但是这种链接是缺乏封装的恶臭(贫血的对象只有数据,没有行为)。你违反了law of Demeter:不要和陌生人说话。

                【讨论】:

                • 那么,PersonDAO.getPerson(123).getAddress().getCity().loadMap() 是错误的吗?
                • JB Nizet 你能评论一下 Nishant 的问题吗?
                • 这是一种气味,不是证据。德墨忒耳法则是一个原则,通常很好遵循,但不一定总是如此。它有优点和缺点。在 Nishant 示例中,您可以直接在 Person 中使用 loadMap(),委托给它的地址。它甚至可以实现一个 Mappable 接口。 OP 的例子有点不同,因为它所做的只是访问数据。
                【解决方案10】:

                这很乱,但如果你只需要在一个地方做,那么我可能会忍受它。否则,我会实现一个隐藏内部路径的 Outer.getSomeString(),因为您的 Outer 类是您作为接口公开的类。

                这还允许您处理其中一个中间内部类为 null 的情况,而无需在每次尝试访问 someString 时进行多次顺序检查。

                【讨论】:

                  【解决方案11】:

                  我认为 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

                  【讨论】:

                    猜你喜欢
                    • 2011-12-18
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-10-02
                    • 1970-01-01
                    • 2023-03-23
                    • 2017-12-12
                    相关资源
                    最近更新 更多