Как сделать этот код «более функциональным»? - PullRequest
0 голосов
/ 03 июля 2018

Как применить к этому 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 голосов
/ 03 июля 2018

Каждая функция должна учитывать последний результат, но мне также нужно передавать разные аргументы в каждой функции.

Возможно, вы ищете карри

Каррирование - это когда вы разбиваете функцию, которая принимает несколько аргументов, на ряд функций, которые принимают часть аргументов.

В это обычно выглядит как использование некоторых аргументов для создания нового объекта, который включает интерфейс для получения оставшихся аргументов. (В более поздних версиях Java вы с большей вероятностью увидите, что используется лямбда, но основная идея та же.)

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;

Итак, в этом примере мы хотим построить группу функций, которые принимают в качестве аргумента значение double и возвращают в результате значение double, которое будет объединено воедино.

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))

Можете ли вы написать пример объявления функций функций computeNeededVolume, computeNeededKgPaint и 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;
    }
}
0 голосов
/ 03 июля 2018

В функциональном программировании вы мыслите в терминах функций (отношение ввода / вывода) вместо алгоритмов (этапы вычислений). Вы избегаете побочных эффектов и состояния.

Таким образом, вместо того, чтобы иметь объект 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;
  }
}

На мой взгляд, локальные переменные хороши, конечно, вы всегда можете разделить функции на более мелкие (и должны), но реальная проблема здесь, на мой взгляд, это состояние, которого вам следует избегать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...