内容简介:假设你已经使用了lambdas流,巨长的Stream的代码如下:以上实现功能是:计算上一年订购产品的次数。现在,只接受频繁订购的产品(> = 10)并返回它们,前提是如果它们没有被逻辑删除或显式隐藏在数据库中。你写完这段代码很快乐地回家了...
假设你已经使用了lambdas流,巨长的Stream的代码如下:
<b>public</b> List<Product> getFrequentOrderedProducts(List<Order> orders) { <b>return</b> orders.stream() .filter(o -> o.getCreationDate().isAfter(LocalDate.now().minusYears(1))) .flatMap(o -> o.getOrderLines().stream()) .collect(groupingBy(OrderLine::getProduct, summingInt(OrderLine::getItemCount))) .entrySet() .stream() .filter(e -> e.getValue() >= 10) .map(Entry::getKey) .filter(p -> !p.isDeleted()) .filter(p -> !productRepo.getHiddenProductIds().contains(p.getId())) .collect(toList());
以上实现功能是:计算上一年订购产品的次数。现在,只接受频繁订购的产品(> = 10)并返回它们,前提是如果它们没有被逻辑删除或显式隐藏在数据库中。
你写完这段代码很快乐地回家了...
但我们会找到你的!管理层无法解雇你,谁可以读懂这堆代码?!谁愿意和你合作?
这段代码最糟糕的是每行返回不同的类型。除非您在IDE将鼠标悬停其中,否则你将看不到这些类型。
清洁代码最重要的规则之一是:小方法。所以,让我们通过查看我们.collect(..)后面看到的代码,将这个长链分成两个方法.stream()。既然你Collect了一个集合中的项目,为什么我们不通过提取一个好的方法名来解释那个集合是什么?
<b>public</b> List<Product> getFrequentOrderedProducts(List<Order> orders) { <b>return</b> getProductCountsOverTheLastYear(orders).entrySet().stream() .filter(e -> e.getValue() >= 10) .map(Entry::getKey) .filter(Product::isNotDeleted) .filter(p -> !productRepo.getHiddenProductIds().contains(p.getId())) .collect(toList()); } <b>private</b> Map<Product, Integer> getProductCountsOverTheLastYear(List<Order> orders) { <b>return</b> orders.stream() .filter(o -> o.getCreationDate().isAfter(LocalDate.now().minusYears(1))) .flatMap(o -> o.getOrderLines().stream()) .collect(groupingBy(OrderLine::getProduct, summingInt(OrderLine::getItemCount))); }
但是,只有这样我们才注意到在第6行,我们可能会在循环中查询外部系统!我的天啊!这是你永远不应该做的事情。
让我们开始流之前先获得hiddenProductIds 列表,我们甚至可以进一步检查产品是否隐藏在Predicate局部变量中:
<b>public</b> List<Product> getFrequentOrderedProducts(List<Order> orders) { List<Long> hiddenProductIds = productRepo.getHiddenProductIds(); Predicate<Product> productIsNotHidden = p -> !hiddenProductIds.contains(p.getId()); <b>return</b> getProductCountsOverTheLastYear(orders).entrySet().stream() .filter(e -> e.getValue() >= 10) .map(Entry::getKey) .filter(Product::isNotDeleted) .filter(productIsNotHidden) .collect(toList());
还有一件事我们可以做:我们可以命名被频繁订购的产品的流,并使其成为Stream类型的变量。众所周知,这些Stream项目实际上并未在此时进行计算评估,而是仅在结束时.collect()进行计算评估。但是,Stream<>有时不鼓励使用变量,因为粗心的开发人员可能会尝试重新使用它(重新遍历它),因此在执行此操作之前,请确保您的团队完全了解这种常见情况。
<b>public</b> List<Product> getFrequentOrderedProducts(List<Order> orders) { List<Long> hiddenProductIds = productRepo.getHiddenProductIds(); Predicate<Product> productIsNotHidden = p -> !hiddenProductIds.contains(p.getId()); Stream<Product> frequentProducts = getProductCountsOverTheLastYear(orders).entrySet().stream() .filter(e -> e.getValue() >= 10) .map(Entry::getKey); <b>return</b> frequentProducts .filter(Product::isNotDeleted) .filter(productIsNotHidden) .collect(toList()); } <p>[...]
这里的想法是通过引入解释变量来避免过多的方法链。这意味着 提取方法甚至使用函数或Stream类型的变量,以使代码尽可能清晰地显示给读者。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 函数式编程之数组的函数式编程
- 函数式编程 – 函数式编程如何影响您的编码风格?
- 纯函数:函数式编程入门
- 深入理解 Java 函数式编程,第 1 部分: 函数式编程思想概论
- 思想交融,Android中的函数式编程(2):什么是函数式编程
- 编程范式 —— 函数式编程入门
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。