В своем недавнем блоге я представил среду форка и соединения 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