Создание объектов Java с помощью статических фабричных методов
Источник: Medium Это руководство объясняет использование статических фабричных методов для создания экземпляров объектов Java. Всякий раз, когда мы хотим, чтобы какой-либо клиент получил экземпляр класса, мы создаем общедоступный конструктор класса (с параметрами или без них) и предоставляем его клиенту для создания объекта вне класса с помощью ключевого слова new. Для этого нам нужно создать внутри нативного класса или в его подклассе public static method (публичный статический метод) со значимым именем и вернуть экземпляр, который нужен клиенту. Примечание. Есть еще один более чистый способ создания экземпляра объекта. Давайте посмотрим его на примере.
public class Player {
String name;
int age;
String game;
private Player(int age, String name, String game) {
this.age = age;
this.name = name;
this.game = game;
}
public static Player getCricketPlayer(int age, String playerName) {
return new Player(age, playerName, "cricket");
}
public static Player getFootballPlayer(int age, String playerName) {
return new Player(age, playerName, "football");
}
public static Player of(int age, String playerName,String game) {
return new Player(age, playerName, game);
}
}
public class Client {
public static void main(String[] args) {
var cricketPlayer = Player.getCricketPlayer(25, "Jon");
System.out.println(cricketPlayer);
var footballPlayer = Player.getFootballPlayer(22, "Igor");
System.out.println(footballPlayer);
}
}
В приведенном выше классе Player есть методы, а именно getCricketPlayer, getFootballPlayer и of. Эти методы дают четкое представление о том, какой тип объекта будет создан и возвращен.
Здесь может возникнуть важный вопрос: нужно ли мне постоянно менять класс Player, если я хочу иметь другой тип объекта? Нет, как правило клиентская программа должна использовать соответствующий метод для создания объекта.
Несколько встроенных в JDK статических фабричных методов для лучшего понимания:
Collections.unmodifiableCollection();
Collections.synchronizeCollection();
LocalDateTime.now();
Ключевые преимущества, которые следует учитывать при использовании статического фабричного метода:- Статические фабричные методы имеют имена, которые, в отличие от конструкторов, могут пояснять код.
Хорошей практикой именования статических фабричных методов является использование таких названий, как of, from, instance, getInstance, newInstance и так далее. - Статические фабричные методы не требуют создания нового объекта при каждом вызове — при необходимости объекты можно кэшировать и использовать повторно.
- Статические фабричные методы могут возвращать подтип своего возвращаемого типа — в частности, могут возвращать объект, класс реализации которого неизвестен вызывающей стороне. Это очень ценная и широко используемая функция во многих фреймворках, использующих интерфейсы в качестве возвращаемого типа статических фабричных методов.
- Статические фабричные методы могут иметь дополнительные журналы для аудита объекта, например времени создания и другие.
Параллельное выполнение зависимых и независимых задач на Java
Источник: Medium Благодаря этой публикации вы узнаете, как параллельно вызывать зависимые и независимые задачи с помощью Java CompletableFuture. В основном три сегодняшние задачи будут вызываться параллельно, так время отклика будет намного меньше. Я собираюсь показать вам, как выполнять параллельное выполнение с помощью класса CompletableFuture и Java 8.Условия задачи
Допустим, у нас есть задача А, задача B и задача C, которые выполняются как зависимые и независимые задачи в параллельном режиме с указанными ниже условиями.- Задача A и задача C могут выполняться независимо.
- Задача B зависит от результата задачи A.
Пример использования
Перед нами бариста, который готовит кофе. Ему нужно выполнить несколько заданий:- Измельчить необходимое количество кофейных зерен (без предшествующих задач).
- Нагреть немного воды (без предшествующих задач).
- Приготовить эспрессо, используя молотый кофе и нагретую воду (зависит от 1 и 2).
- Вспенить немного молока (без предшествующих задач).
- Смешать вспененное молоко и эспрессо (зависит от 3,4).
Пример кода
package com.demo.myexamples.service;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class Barrista
{
// количество потоков, используемых в executor
static final int NOTHREADS = 3;
// время каждого задания
static final int HEATWATER = 1000;
static final int GRINDBEANS = 1000;
static final int FROTHMILK = 1000;
static final int BREWING = 1000;
static final int COMBINE = 1000;
// метод имитации работы (приостановка текущего потока без выдачи проверенного исключения)
public static void pause(long t)
{
try
{
Thread.sleep(t);
}
catch(Exception e)
{
throw new Error(e.toString());
}
}
// задание по нагреву воды
static class HeatWater implements Supplier<String>
{
@Override
public String get()
{
System.out.println("Heating Water");
pause(HEATWATER);
return "hot water";
}
}
// задание по измельчению зерен
static class GrindBeans implements Supplier<String>
{
@Override
public String get()
{
System.out.println("Grinding Beans");
pause(GRINDBEANS);
return "grinded beans";
}
}
// задание вспенить немного молока
static class FrothMilk implements Supplier<String>
{
@Override
public String get()
{
System.out.println("Frothing some milk");
pause(FROTHMILK);
return "some milk";
}
}
// задание сварить немного кофе
static class Brew implements BiFunction<String,String, String>
{
@Override
public String apply(String groundBeans, String heatedWater)
{
System.out.println("Brewing coffee with " + groundBeans + " and " + heatedWater);
pause(BREWING);
return "brewed coffee";
}
}
// задание смешать кофе и молоко
static class Combine implements BiFunction<String,String, String>
{
@Override
public String apply(String frothedMilk, String brewedCoffee)
{
System.out.println("Combining " + frothedMilk + " "+ brewedCoffee);
pause(COMBINE);
return "Final Coffee";
}
}
public static void main(String[] args)
{
ExecutorService executor = Executors.newFixedThreadPool(NOTHREADS);
long startTime = System.currentTimeMillis();
try
{
// создаем все задачи и позволяем executor обрабатывать порядок выполнения
CompletableFuture<String> frothMilk = CompletableFuture.supplyAsync(new FrothMilk(), executor);
CompletableFuture<String> heatWaterFuture = CompletableFuture.supplyAsync(new HeatWater(), executor);
CompletableFuture<String> grindBeans = CompletableFuture.supplyAsync(new GrindBeans(), executor);
CompletableFuture<String> brew = heatWaterFuture.thenCombine(grindBeans, new Brew());
CompletableFuture<String> coffee = brew.thenCombine(frothMilk, new Combine());
// готовое кофе
System.out.println("Here is the coffee:" + coffee.get());
// анализ времени:
System.out.println("\n\n");
System.out.println("Time taken using multi-threaded:\t\t" + (System.currentTimeMillis() - startTime)/1000.0);
// вычислить максимально возможное время:
long longestTime = HEATWATER + GRINDBEANS + FROTHMILK + BREWING + COMBINE;
System.out.println("Time taken using single-threaded thread:\t" + longestTime/1000.0);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
executor.shutdown();
}
}
}
Вывод:
Frothing some milk
Heating Water
Grinding Beans
Brewing coffee with hot water and grinded beans
Combining brewed coffee some milk
Here is the coffee:Final Coffee
Time taken using multi-threaded: 3.027
Time taken using single-threaded thread: 5.0
В нашем случае общее время выполнения всех пяти задач, вызываемых в однопоточном режиме, составляет 1000 + 1000 + 1000 + 1000 + 1000 = 5000 мс. В реальной жизни, если вы ждете готовое кофе, то вам наверняка придется подождать выполнение задачи, которая занимает больше всего времени. Но если представить, что все это выполняется в многопоточном режиме, то общее время составит 3027 мс.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ