可选的monad和Java中的Demeter法则

问题描述 投票:7回答:5

当我查看一些代码时,我遇到了这个代码片段。

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类添加一个方法hasAddress(),但这种方法克服了需要有一个Optional<Address>,IMO。

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

编辑:我错过了在map方法中指定我不想返回地址。

java refactoring optional law-of-demeter
5个回答
3
投票

好吧,你自己总结得很好:如果你想通过引入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。


最后,您需要决定此特定示例是否会将流的功能移动到User类中。两者都有效,可读性/可维护性/清洁度取决于:

  • 具体案例
  • 如何将此代码暴露给其他模块
  • User类中需要的委托方法数
  • 您的体系结构(例如,如果您在UserDao类中,您是否真的想要将数据库访问移动到您的用户POJO类?难道DAO不是为了这个目的而制作的吗?这是否符合“密切相关”并允许违规“只有一个点”的规则?)
  • ...

1
投票

不知道它是否更干净(因为你需要使用getAddress返回一个可选的事实),但在Java 9中你可以这样做:

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

要么

users.stream()
    .flatMap(user -> user.getAddress().stream())
    .map(/* Some code */)

0
投票

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

这里有两个不同的用例:

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

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


0
投票

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

因此,如果它对于要发出的地址有效,则返回Optional处理空值的情况,并可用于确定是否存在。检查可选的“空虚”在我看来与检查0的整数相同 - 你没有访问某个其他对象的属性,只是你给定的对象的属性。

如果只允许知道存在,那么isPresent方法可能更好。当然,如果您正在处理SQL值,那么可能需要Optional来处理SQL NULL值。

否则,为什么这个过滤发生在这里?


0
投票

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

由于用户具有地址,因此他应该能够回答有关地址的问题而不必暴露它。那样你就满足了LoD

© www.soinside.com 2019 - 2024. All rights reserved.