io流,装饰者模式_流与装饰器

io流,装饰者模式

几年前, Streams APIlambda表达式一起在Java 8中引入。 作为一名训练有素的Java专家,我尝试在我的一些项目中使用此新功能,例如herehere 。 我真的不喜欢它,然后又回到了好的老房子里。 此外,我创建了装饰库Cactoos来取代Guava ,而Guava在很多地方都不是很好。

这是一个原始示例。 假设我们有一些来自某些数据源的测量值,它们都是零到一之间的数字:

Iterable<Double> probes;

现在,我们只需要显示它们的前10个,忽略零和一,然后将它们重新缩放为(0..100) 。 听起来很容易,对吧? 有三种方法可以实现:过程式,面向对象和Java 8方法。 让我们从过程的方式开始:

int pos = 0;
for (Double probe : probes) {
  if (probe == 0.0d || probe == 1.0d) {
    continue;
  }
  if (++pos > 10) {
    break;
  }
  System.out.printf(
    "Probe #%d: %f", pos, probe * 100.0d
  );
}

为什么这是一种程序方式? 因为这势在必行。 为什么势在必行? 因为它是程序性的。 不,我在开玩笑。

这是当务之急,因为我们正在向计算机发出有关将哪些数据放入何处以及如何对其进行迭代的指令。 我们不是在声明结果,而是必须构建它。 它可以工作,但不是真正可扩展的。 我们无法参与该算法并将其应用于另一个用例。 我们无法真正轻松地对其进行修改,例如,要从两个来源而不是一个地方获取数字,等等。这是程序性的。 说够了。 不要这样

现在,Java 8为我们提供了Streams API ,该API应该提供一种实现此目的的功能方法。 让我们尝试使用它。

首先,我们需要创建一个Stream的实例, Iterable 不允许我们直接获取它。 然后,我们使用流API来完成这项工作:

StreamSupport.stream(probes.spliterator(), false)
  .filter(p -> p == 0.0d || p == 1.0d)
  .limit(10L)
  .forEach(
    probe -> System.out.printf(
      "Probe #%d: %f", 0, probe * 100.0d
    )
  );

这将起作用,但将对所有探针说出Probe #0 ,因为forEach()不适用于索引。 目前是没有这样的事forEachWithIndex()Stream界面的Java 8(和Java 9的 )。 这是使用原子计数器的解决方法

AtomicInteger index = new AtomicInteger();
StreamSupport.stream(probes.spliterator(), false)
  .filter(probe -> probe == 0.0d || probe == 1.0d)
  .limit(10L)
  .forEach(
    probe -> System.out.printf(
      "Probe #%d: %f",
      index.getAndIncrement(),
      probe * 100.0d
    )
  );

“那是怎么了?” 你可能会问。 首先,看看在Stream接口中找不到正确的方法时,我们遇到麻烦的Stream 。 我们立即摆脱了“流式”范式,回到了良好的旧程序全局变量(计数器)。 其次,我们真的看不到那些filter()limit()forEach()方法内部发生了什么。 它们如何工作? 该文档说,这种方法是“声明性的”,并且Stream接口中的每个方法都返回某个类的实例。 他们是什么班? 只看这段代码,我们一无所知。

此流API的最大问题是Stream接口,它非常庞大!

这两个问题是联系在一起的。 此流API的最大问题是接口Stream –很大。 在撰写本文时,有43种方法。 在一个界面中四十三! 从SOLID到后来的更严格的原则 ,这都与面向对象编程的每条原则 背道而驰

实现相同算法的面向对象方法是什么? 这就是我如何使用Cactoos做到的 ,这只是一个集合 原始 简单的Java类:

new And(
  new Mapped<Double, Scalar<Boolean>>(
    new Limited<Double>(
      new Filtered<Double>(
        probes,
        probe -> probe == 0.0d || probe == 1.0d
      ),
      10
    ),
    probe -> () -> {
      System.out.printf(
        "Probe #%d: %f", 0, probe * 100.0d
      );
      return true;
    }
  ),
).value();

让我们看看这里发生了什么。 首先, Filtered装饰了我们的可迭代probes以从中取出某些项。 注意, Filtered实现了Iterable 。 然后, Limited (也是一个Iterable )仅取出前十个项目。 然后, Mapped将每个探针转换为Scalar<Boolean>的实例,该实例执行行打印。

最后, And的实例遍历“标量”列表,并要求每个标量返回boolean 。 他们打印行并返回true 。 既然它是trueAnd使用下一个标量进行下一次尝试。 最后,其方法value()返回true

但是,等等,没有索引。 让我们添加它们。 为了做到这一点,我们只使用另一个名为AndWithIndex类:

new AndWithIndex(
  new Mapped<Double, Func<Integer, Boolean>>(
    new Limited<Double>(
      new Filtered<Double>(
        probes,
        probe -> probe == 0.0d || probe == 1.0d
      ),
      10
    ),
    probe -> index -> {
      System.out.printf(
        "Probe #%d: %f", index, probe * 100.0d
      );
      return true;
    }
  ),
).value();

现在,我们将探针映射到Func<Integer, Boolean> ,而不是Scalar<Boolean> Func<Integer, Boolean>以使其接受索引。

这种方法的优点在于所有类和接口都很小,这就是为什么它们很容易组合的原因。 为了限制探针的迭代,我们用Limited装饰它; 为了使它过滤,我们用Filtered装饰它; 为了做其他事情,我们创建一个新的装饰器并使用它。 我们并没有像Stream这样的单一接口。

最重要的是,装饰器是一种用于修改集合行为的面向对象的工具,而流是我什至找不到其名称的其他东西。

PS顺便说一下,这就是在Guava的Iterables的帮助下可以实现相同算法的方式:

Iterable<Double> ready = Iterables.limit(
  Iterables.filter(
    probes,
    probe -> probe == 0.0d || probe == 1.0d
  ),
  10
);
int pos = 0;
for (Double probe : probes) {
  System.out.printf(
    "Probe #%d: %f", pos++, probe * 100.0d
  );
}

这是一些面向对象和功能样式的怪异组合。

翻译自: https://www.javacodegeeks.com/2017/10/streams-vs-decorators.html

io流,装饰者模式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值