当我查看一些代码时,我遇到了这个代码片段。
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
方法中指定我不想返回地址。
好吧,你自己总结得很好:如果你想通过引入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类中。两者都有效,可读性/可维护性/清洁度取决于:
不知道它是否更干净(因为你需要使用getAddress
返回一个可选的事实),但在Java 9中你可以这样做:
users.stream()
.map(User::getAddress)
.flatMap(Optional::stream)
.map(/* Some code */)
要么
users.stream()
.flatMap(user -> user.getAddress().stream())
.map(/* Some code */)
我相信你已经自己回答了这个问题。
这里有两个不同的用例:
第一个用例可以通过添加方法hasAddress()
来解决,就像你说的那样。可以使用Optional
来解决第二个用例来包装地址。没有什么不妥。
LoD要求您考虑的是地址的价值是否需要发出(即,请求/访问)。如果答案是肯定的 - 那么你需要处理空值或空值的情况;否则,您可以考虑是否可以请求存在地址。
因此,如果它对于要发出的地址有效,则返回Optional处理空值的情况,并可用于确定是否存在。检查可选的“空虚”在我看来与检查0的整数相同 - 你没有访问某个其他对象的属性,只是你给定的对象的属性。
如果只允许知道存在,那么isPresent方法可能更好。当然,如果您正在处理SQL值,那么可能需要Optional来处理SQL NULL值。
否则,为什么这个过滤发生在这里?
另一种方法是将计算移动到用户类。
由于用户具有地址,因此他应该能够回答有关地址的问题而不必暴露它。那样你就满足了LoD