В своем недавнем блоге я представил среду форка и соединения Java 7. В этом блоге представлена небольшая структура поверх необработанного форка и соединения. Фреймворк реализует разложимый шаблон ввода (dip), который возник из-за моей собственной лени, когда я пару раз использовал фреймворк. Я понял, что пишу один и тот же код каждый раз, когда реализую несколько иной вариант использования. И вы знаете, давайте напишем немного программного обеспечения, которое я могу использовать повторно. Создана структура разложимых входных шаблонов.
Вы можете скачать бинарный файл здесь . API-документация здесь организована . И источники также доступны здесь . Теперь, что отличается, когда вы используете эту платформу? Я бы сказал, что разница в том, что dip-каркас следует хорошим принципам проектирования ОО , например принципу открытого-закрытого типа, который гласит: «Модуль должен быть открыт для расширения, но закрыт для модификации». Другими словами, я разделил проблемы в сценарии разветвления и соединения, чтобы сделать все более гибким и легко изменяемым. В моем последнем блоге я представил фрагмент кода, который иллюстрировал, как использовать простой fork и join для расчета предложений по страхованию автомобилей. Давайте посмотрим, как это можно сделать, используя мою каркасную конструкцию.
Входные данные для расчета предложения — это, ну, в общем, список предложений ? В структуре dip вы вводите вход ForkJoinTask в подкласс DecomposableInput. Название происходит от того факта, что входные данные для ForkJoinTask разложимы. Вот фрагмент:
import java.util.ArrayList;
import java.util.List;
import com.schlimm.forkjoindip.DecomposableInput;
public class ListOfProposals extends DecomposableInput<List<Proposal>> {
public ListOfProposals(List<Proposal> proposals) {
super(proposals);
}
@Override
public boolean computeDirectly() {
return rawInput.size()==1;
}
@Override
public List<DecomposableInput<List<Proposal>>> decompose() {
int split = rawInput.size() / 2;
List<DecomposableInput<List<Proposal>>> decomposedListOfProposals = new ArrayList<>();
decomposedListOfProposals.add(new ListOfProposals(rawInput.subList(0, split)));
decomposedListOfProposals.add(new ListOfProposals(rawInput.subList(split, rawInput.size())));
return decomposedListOfProposals;
}
}
Класс оборачивает необработанный ввод в ForkJoinTask и предоставляет метод, как этот ввод может быть разложен. Кроме того, он предоставляет метод computeDirectly (), который может решить, нуждается ли этот вход в дальнейшей декомпозиции, чтобы быть достаточно маленьким для прямого вычисления.
Результатом расчета предложения является список карт цен. Если у вас есть четыре предложения ввода, вы получите список из четырех карт с различными ценами. В рамках DIP вы оборачиваете вывод в подкласс ComposableResult.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.schlimm.forkjoindip.ComposableResult;
public class ListOfPrices extends ComposableResult<List<Map<String, Double>>> {
public ListOfPrices(List<Map<String, Double>> firstpeace) {
super(firstpeace);
}
@Override
public ComposableResult<List<Map<String, Double>>> compose(ComposableResult<List<Map<String, Double>>> result) {
List<Map<String, Double>> listOfPrices = new ArrayList<>();
listOfPrices.addAll(result.getRawResult());
listOfPrices.addAll(rawResult);
ListOfPrices prices = new ListOfPrices(listOfPrices);
return prices;
}
@Override
public String toString() {
return getRawResult().toString();
}
}
Класс реализует метод compose, который может составлять атомарный результат вычисления в существующий необработанный результат. Он возвращает экземпляр ComposableResult, который содержит новую композицию.
Я согласен, что это немного невнимательно. Мало того, что параллелизм по своей сути сложен. Я также помещаю другую абстракцию на это. Но как только вы используете каркас, вы поймете силу. Так что следите за обновлениями, мы почти закончили ?
Теперь у вас есть вход и выход, и последнее, что вам нужно, это вычислительный объект. В моем примере это двигатель ценообразования. Чтобы подключить механизм ценообразования к платформе DIP, вам необходимо реализовать подкласс ComputationActivityBridge.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.schlimm.forkjoindip.ComposableResult;
import com.schlimm.forkjoindip.ComputationActivityBridge;
import com.schlimm.forkjoindip.DecomposableInput;
public class PricingEngineBridge extends ComputationActivityBridge<List<Proposal>, List<Map<String, Double>>> {
private PricingEngine engine = new PricingEngine();
@Override
public ComposableResult<List<Map<String, Double>>> compute(DecomposableInput<List<Proposal>> input) {
Map<String, Double> result = engine.calculatePrices(input.getRawInput().get(0));
List<Map<String, Double>> priceList = new ArrayList<>();
priceList.add(result);
return new ListOfPrices(priceList);
}
}
PricingEngineBridge реализует метод вычисления, который вызывает механизм ценообразования. Он переводит DecomposableInput во входные данные, которые принимает механизм оценки. И он создает экземпляр ComposableResult, который содержит выходные данные механизма оценки.
Последнее, что нужно сделать, это начать работу.
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
import com.schlimm.forkjoindip.ComposableResult;
import com.schlimm.forkjoindip.GenericRecursiveTask;
public class ForkJoinTaskExample_Generic {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List<Proposal> proposalsList = new ArrayList<>();
proposalsList.add(new Proposal("Niklas", "Schlimm", "7909","AAL", true, true, true));
proposalsList.add(new Proposal("Andreas", "Fritz", "0005", "432", true, true, true));
proposalsList.add(new Proposal("Christian", "Toennessen", "0583", "442", true, true, true));
proposalsList.add(new Proposal("Frank","Hinkel", "4026", "AAA", true, true, true));
ListOfProposals proposals = new ListOfProposals(proposalsList);
GenericRecursiveTask task = new GenericRecursiveTask(proposals, new PricingEngineBridge());
ForkJoinPool pool = new ForkJoinPool();
System.out.println(new Date());
ComposableResult<List<Map<String, Double>>> result = pool.invoke(task);
System.out.println(result);
System.out.println(new Date());
}
}
Пример создает экземпляр GenericRecursiveTask и передает в качестве входных данных ListOfProposals, а также PricingEngineBrige. Если вы передадите это в ForkJoinPool, вы получите экземпляр ListOfPrices в качестве вывода.
В чем преимущество при использовании dip-framework? Например:
— вы можете передать произвольные входные данные для обработки в GenericRecursiveTask, реализовав подкласс DecomposableInput
— вы можете реализовать свой собственный настраиваемый RecursiveTask таким же образом, как я реализовал GenericRecursiveTask и передать предложения и PricingEngineBridge для этой задачи
— вы можете реализовать собственный ForkAndJoinProcess и используйте это, создав подкласс GenericRecursiveTask: таким образом вы можете контролировать создание подзадач и их распределение по потокам
— вы можете поменять процесс обработки (здесь: PricingEngineBridge), внедрив собственный ComputationActivityBridge и попробовать альтернативные механизмы ценообразования или сделать что-то совершенно другое, чем вычисление цен …
Я думаю, что я высказал свою точку зрения: все закрыто для модификации, но открыто для расширения сейчас.
Полный пример кода находится
здесь, в моем репозитории git .
Дайте мне знать, если вам это нравится. Ждем критических и приятных комментариев.
Ура, Никлас
От http://niklasschlimm.blogspot.com/2011/12/java-7-fork-and-join-decomposable-input.html