信息隐藏和函数式编程风格

3
我正在开发一个名为 Simulator 的简单类,它可以将一组 Simulation 应用于某个输入。对于每个输入,模拟可以根据输入必须满足的某些条件生成输出或不生成输出。 Simulator 生成的结果是一个输出列表。
以下是代码。
class Simulator {
    final List<Simulation> simulations;

    // Some initialization code...

    List<Ouput> execute(Input input) {
        return simulations
            .stream()
            .filter(s -> s.processable(input))
            .map(s -> s.prepareOutput(input))
            .collect(Collectors.toList());
    }
}

如您所见,我首先验证输入是否可由“Simulation”处理,过滤掉不可处理的模拟器,然后将这些模拟器应用于输入。
从面向对象的角度来看,我正在公开“Simulation”类的内部。由“processable”方法执行的检查操作应该隐藏在“prepareOutput”方法中。
然而,通过让“Simulator”看到“processable”,我可以采用更实用的功能方法,这非常方便。
哪种方法更好?我是否忽略了其他解决方案?

2
如果您认为您正在暴露内部,那么为什么还要这样做呢?还有一些问题需要思考:如果我使用 someSimulation.prepareOutput(null) 调用您的任何模拟,会发生什么?那个是否可以处理?或者,如果我使用不可由给定模拟处理的输入参数调用 prepareOutput,我需要从外部了解或检查它的可处理性吗?返回 null 还是抛出异常?是使用 Optional 选项吗? - Roland
2个回答

4

由于你的类Simulation已经暴露了可能会失败的操作prepareOutput,因此当你提供processable方法来预先检测prepareOutput操作是否会失败时,不会有额外的曝光。实际上,只要预先计算不会太昂贵,提供这样的检查是一个很好的API设计。

你仍然可以考虑在Simulation类内部提供批量处理操作。

public class Simulation {
    public Output prepareOutput(Input input) {
        …
    }
    public static List<Output> prepareWhenPossible(List<Simulation> list, Input input) {
        return simulations.stream()
            .filter(s -> s.processable(input))
            .map(s -> s.prepareOutput(input))
            .collect(Collectors.toList());
    }
}

重要的是要向调用者明确,它将跳过无法进行操作的元素,而不是实现“全有或全无”的行为。
如果实现成本低廉,这仍然不排除暴露可处理的processable。这并不是一个不可能的操作,因为始终可以只调用prepareOutput并丢弃结果以找出操作是否可行。为此拥有一个processable方法更加清晰简洁。

我认为你找到了问题所在:如果processable方法对其他类型可用,那么prepareOutput方法内部调用它是很重要的。从这个角度来看,prepareOutput方法无论如何都应该返回一个Optional<Output> - riccardo.cardin

1
如果你需要隐藏 "processable",为什么不采用一种不同的方法:
 Optional<Output> prepareOutput(Input input) {

      boolean isProcessable = processable(input); // processable is private

      if(isProcessable){
            // prepare Output
            return Optional.of(Output);   
      }
      return Optional.empty();

 }

然后是这样的内容:
  List<Ouput> execute(Input input) {
    return simulations
        .stream()
        .map(s -> s.prepareOutput(input))
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toList());
 }

1
但这只是从技术上隐藏了“processable”,而不是语义上,因为每个人都可以编写一个方法 boolean processable(Simulation s, Input i) { return s.prepareOutput(i).isPresent(); },所以隐藏“processable”所实现的一切,就是使得这个测试可能不那么高效。 - Holger
1
@Holger 谢谢,好观点。我以为只是关于隐藏那个方法。你的答案对于批量处理非常有帮助。 - Eugene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接