如何使此代码“更具功能性”?

时间:2018-07-03 11:01:05

标签: java functional-programming

如何将这种更具功能性的样式应用于此OOP Java代码?

class PaintCarCost
  Car car;
  Paint paint;
  PaintMaterialsMixture mixture;

  double cost () {
    double volumeToPaint = ... // calculate from car and mixture
    double necessaryVolumePaint = ... // calculate from volumeToPaint, car and paint
    double necessaryKgPaint = ... // calculate from necessaryVolumePaint and paint
    double cost = ... // calculate from necessaryKgPaint and paint
    return cost;
  }
}

我曾想过将每个计算拆分成一个函数:

private BiFunction <Car,PaintMaterialsMixture,Double> volumeToPaint = ...
private BiFunction <Double,Car,Paint> necessaryVolumePaint = ...
private BiFunction <Double,Paint> necessaryKgPaint = ...

但是我不知道如何以一种优雅的方式将它们全部链接起来以产生最终成本。每个函数都必须考虑最后的结果,但是我也需要在每个函数中传递不同的参数。

2 个答案:

答案 0 :(得分:1)

在函数式编程中,您从函数(输入/输出关系)而不是算法(计算步骤)的角度考虑。您可以避免副作用和状态。

因此,您拥有一个无状态的PaintCarCost函数,而不是使用一个有状态的PaintCarCost对象:

class PaintCarCost
  double cost (Car car, Paint paint, PaintMaterialsMixture mixture) {
    double volumeToPaint = ... // calculate from car and mixture
    double necessaryVolumePaint = ... // calculate from volumeToPaint, car and paint
    double necessaryKgPaint = ... // calculate from necessaryVolumePaint and paint
    double cost = ... // calculate from necessaryKgPaint and paint
    return cost;
  }
}

我认为局部变量很好,您当然可以始终将函数拆分为较小的函数(并且应该如此),但我认为这里真正的问题是应避免的状态。

答案 1 :(得分:1)

  

每个函数都必须考虑最后的结果,但是我也需要在每个函数中传递不同的参数。

您可能正在寻找currying

  

咖喱是指将一个包含多个参数的函数分解为一系列包含一部分参数的函数。

中,这通常看起来像是使用一些参数来创建新对象,该对象包括用于接收其余参数的接口。 (在Java的最新版本中,您更可能会看到lambda的用法,但基本思想是相同的。)

double volumeToPaint = ... // calculate from car and mixture
double necessaryVolumePaint = ... // calculate from volumeToPaint, car and paint
double necessaryKgPaint = ... // calculate from necessaryVolumePaint and paint
double cost = ... // calculate from necessaryKgPaint and paint
return cost;

因此,在此示例中,我们要构建一堆将双精度作为参数并返回双精度的函数,这些函数将全部链接在一起。

DoubleUnaryOperator computeNecessaryVolume = someFactory.createComputeNecessaryVolume(car, paint);
DoubleUnaryOperator computeNecessaryKgPaint = someFactory.createComputeNecessaryKgPaint(paint);
DoubleUnaryOperator computeCost = someFactory.createComputeCost(paint);

double volumeToPaint = computeVolume(car, mixture);
double necessaryVolumeToPaint = computeNecessaryVolume.applyAsDouble(volumeToPaint);
double necessaryKgPaint = computeNecessaryKgPaint.applyAsDouble(necessaryVolumeToPaint);
double cost = computeCost.applyAsDouble(necessaryKgPaint);

这是基本思想;您可以通过将函数链接到一个管道中来“简化”它,就像我们对流进行处理一样。...

cost = computeVolume(car, mixture)
.map(volumeToPaint -> computeNecessaryVolume.applyAsDouble(volumeToPaint))
.map(necessaryVolumeToPaint -> computeNecessaryKgPaint.applyAsDouble(necessaryVolumeToPaint))
.map(necessaryKgPaint -> computeCost.applyAsDouble(necessaryKgPaint) )

但是您更有可能只是将这些功能组合在一起。

DoubleUnaryOperation doIt = computeNecessaryVolume
                            .andThen(computeNecessaryKgPaint) 
                            .andThen(computeCost);

double cost = doIt.applyAsDouble(computeVolume(car, mixture))
  

您能写一些有关computeNecessaryVolume,computeNecessaryKgPaint和computeCost函数的函数声明的示例吗?

class ComputeNecessaryVolume implements DoubleUnaryOperator {
    final Car car;
    final Mixture mixture;

    ComputeNecessaryVolume(Car car, Mixture mixture) {
        this.car = car;
        this.mixture = mixture;
    }

    @Override
    double applyAsDouble(double volumeToPaint) {
        // Do whatever you were doing before with car, mixture, and volumeToPaint
        double necessaryVolumeToPaint = ...

        return necessaryVolumeToPaint;
    }
}