Статьи

Java 7: раскладываемый и объединяемый шаблон ввода

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