代码之家  ›  专栏  ›  技术社区  ›  riccardo.cardin

Java中的可选单子和德米特定律

  •  8
  • riccardo.cardin  · 技术社区  · 7 年前

    当我复习一些代码时,我偶然发现了这个片段。

    List<User> users = /* Some code that initializes the list */;
    users.stream()
         .filter(user -> user.getAddress().isPresent())
         .map(/* Some code */)
    // And so on...
    

    方法的调用 user.getAddress() 返回一个 Optional<Address> . 根据著名的德米特定律(LoD),上述代码并不干净。然而,我不知道如何重构它以使其更干净。

    User a类方法 hasAddress() ,但该方法克服了具有 可选(<);地址(>); ,IMO。

    我应该如何重构上述代码?在这种情况下,是否值得满足LoD?

    编辑 :我没有在 map 方法我不想返回地址。

    5 回复  |  直到 7 年前
        1
  •  3
  •   Malte Hartwig Darren Gunning    7 年前

    好吧,你自己总结得很好:如果你想通过引入 hasAddress() ,为什么返回 Optional .

    Reading into what the LoD says ,它谈到对“密切相关”单元的“有限”知识。对我来说,这听起来像一个灰色区域,但更进一步,它还提到了“只有一个点”规则。尽管如此,我还是同意对您的问题的评论,即空检查(或 isPresent() )完全可以(见鬼,真正的空检查技术上甚至不需要点;P)。

    如果您想真正封装更多内容,可以删除 getAddress() 完全提供:

    class User {
        private Optional<Address> address;
    
        boolean hasAddress() {
            return address.isPresent();
        }
    
        // still exposes address to the consumer, guard your properties
        void ifAddressPresent(Consumer<Address> then) {
            address.ifPresent(then::accept);
        }
    
        // does not expose address, but caller has no info about it
        void ifAddressPresent(Runnable then) {
            address.ifPresent(address -> then.run());
        }
    
        // really keep everything to yourself, allowing no outside interference
        void ifAddressPresentDoSomeSpecificAction() {
            address.ifPresent(address -> {
                // do this
                // do that
            });
        }
    }
    

    但是,正如评论者指出的那样:这值得/必要吗?所有这些定律/原则很少是绝对的,更多的是指导原则,而不是教条。在这种情况下,可能需要平衡LoD和KISS。


    最后,由您决定将流的功能移动到用户类中是否有利于此特定示例。两者均有效,可读性/可维护性/清洁度取决于:

    • 具体案例
    • 此代码对其他模块的公开程度
    • 用户类中需要的委托方法的数量
    • 您的体系结构(例如,如果您在UserDao类中,您真的想将数据库访问移到您的用户POJO类中吗?DAO不正是为了这个目的吗?这是否符合“密切相关”的条件,并且允许违反“只有一个点”规则?)
    • ...
        2
  •  1
  •   pron    7 年前

    getAddress

    users.stream()
        .map(User::getAddress)
        .flatMap(Optional::stream)
        .map(/* Some code */)
    

    users.stream()
        .flatMap(user -> user.getAddress().stream())
        .map(/* Some code */)
    
        3
  •  0
  •   Hugo Madureira    7 年前

    我相信你自己已经回答了这个问题。

    这里有两个不同的用例:

    1. 确定用户是否有地址
    2. 以空安全方式访问用户地址

    第一个用例可以通过添加方法来解决 hasAddress() ,就像你说的。 第二个用例可以使用 Optional 包装地址。这没什么错。

        4
  •  0
  •   Jay    7 年前

    LoD要求您考虑的是是否需要给出(即请求/访问)地址的值。如果答案是肯定的,那么您需要处理null或空值的情况;否则,您可以考虑是否可以请求地址的存在。

    因此,如果它对要给出的地址有效,那么返回一个Optional将处理空值情况,并可用于确定是否存在。在我看来,检查一个可选项是否为“空”与检查一个整数是否为0是一样的——你并没有访问其他对象的属性,只是访问了你得到的对象的属性。

    否则,为什么这里会发生这种过滤?

        5
  •  0
  •   user3237183 user3237183    7 年前

    另一种方法是将计算转移到用户类。

    由于用户拥有该地址,他应该能够回答有关该地址的问题,而不必公开该地址。这样你就能满足LoD