Применение функциональных интерфейсов для Stratery pattern
Есть мапер на mapstruct который в зависимости от типа тикета вызывает разные map методы и все это реализовано через if else условия. Пытаюсь переписать это на Strategy pattern или что-то похожее на него, но в данном контексте все время что-то не сходится.
// Хотелось бы как-то на месте инициализировать список условий
Map<Set<TicketType>, Function<TicketRequestDto, Ticket>> ticketMapStrategies = new ConcurrentHashMap<>(
... Как-то наполнить на месте, но как? ...
);
default Ticket ticketRequestDtoToTicket(TicketRequestDto dto) {
TicketType type = dto.getType();
if (type.equals(TicketType.EMERGENCY_STOP) || type.equals(TicketType.SETTING_UPLOAD)
|| dto.getType().equals(TicketType.SETTING_DOWNLOAD)) {
return ticketEmergencyStopRequestDtoToTicket(dto);
} else if (type.equals(TicketType.DEPOSIT_INCOME) || type.equals(TicketType.DEPOSIT_OUTCOME)) {
return ticketAccountTransferRequestDtoToTicket(dto);
} else if (type.equals(TicketType.PARTNER_REQUEST)) {
return ticketPartnershipRequestDtoToTicket(dto);
} else {
return ticketInfoRequestDtoToTicket(dto);
}
}
@Mapping(target = "instance.id", source = "instanceId")
@Mapping(target = "recipient.id", source = "recipientId")
Ticket ticketEmergencyStopRequestDtoToTicket(TicketRequestDto dto);
@Mapping(target = "account.id", source = "accountId")
@Mapping(target = "recipient.id", source = "recipientId")
Ticket ticketAccountTransferRequestDtoToTicket(TicketRequestDto dto);
@Mapping(target = "sender.id", source = "senderId")
@Mapping(target = "recipient.id", source = "recipientId")
Ticket ticketPartnershipRequestDtoToTicket(TicketRequestDto dto);
Ticket ticketInfoRequestDtoToTicket(TicketRequestDto dto);
Хотелось бы как-то на месте инициализировать список условий а в ticketRequestDtoToTicket просто доставать по ключу. Что-то вроде:
Set<TicketType> key = ticketMapStrategies.keySet().stream()
.filter(set -> set.contains(dto.getType()))
.findFirst()
.orElse(new HashSet<>());
return ticketMapStrategies.get(key).apply(dto);
Или что-то покомпактнее если возможно.
Ну или что-то в таком духе. Как это можно реализовать учитывая что все это происходит в интерфейсе?
Java 11
Ответы (2 шт):
как пор мне, ConcurrentHashMap вам не нужна - у вас же не происходит добавление в мапу в нескольких потоках.
Можно примерно так (если java11+)
Map<Set<TicketType>, Function<TicketRequestDto, Ticket>> map = Map.of(
Set.of(TicketType.EMERGENCY_STOP, TicketType.SETTING_UPLOAD, TicketType.SETTING_DOWNLOAD), this::ticketEmergencyStopRequestDtoToTicket,
Set.of(TicketType.DEPOSIT_INCOME, TicketType.DEPOSIT_OUTCOME), this::ticketAccountTransferRequestDtoToTicket,
Set.of(TicketType.PARTNER_REQUEST), this::ticketPartnershipRequestDtoToTicket
);
default Ticket ticketRequestDtoToTicket(TicketRequestDto dto) {
return map.entrySet()
.stream()
.filter(entry -> entry.getKey().contains(dto.getType))
.map(entry -> entry.getValue())
.findFirst()
.orElseGet(this::ticketInfoRequestDtoToTicket)
.apply(dto);
}
Если же у вас java8, то можно проинциализировать на месте
Map<Set<TicketType>, Function<TicketRequestDto, Ticket>> map = new HashMap() {{
map.put(new HashSet(Arrays.asList(перечислить нужные типы тикета)), this::ticketEmergencyStopRequestDtoToTicket);
//вставить все остальные подобным способом.
}}
Если в проект подключена либа guava, то там можно воспользовать ImmutableMap:
Map<Set<TicketType>, Function<TicketRequestDto, Ticket>> map = ImmutableMap.of(
new HashSet(Arrays.asList(перечислить нужные типы тикета)), this::ticketEmergencyStopRequestDtoToTicket),
// добавить все остальное
);
P.S.
Вообще, тут по сути мапа как структура не нужна - мы ни разу не вызываем метод .get() у мапы => мапа как структура тут не нужна. Ее спокойно можно заменить на лист пар (List<Pair<Set<TicketType>, Function<TicketRequestDto, Ticket>>>)
Пока самый приемлимый вариант который удалось найти, - это использовать @PostConstruct и в нем заполнять мапу.
Map<Set<TicketType>, Function<TicketRequestDto, Ticket>> ticketMapStrategies = new ConcurrentHashMap<>();
@PostConstruct
default void init() {
ticketMapStrategies.put(
Set.of(TicketType.INFO),
this::ticketInfoRequestDtoToTicket);
ticketMapStrategies.put(
Set.of(TicketType.PARTNER_REQUEST),
this::ticketPartnershipRequestDtoToTicket);
ticketMapStrategies.put(
Set.of(TicketType.DEPOSIT_INCOME, TicketType.DEPOSIT_OUTCOME),
this::ticketAccountTransferRequestDtoToTicket);
ticketMapStrategies.put(
Set.of(TicketType.EMERGENCY_STOP, TicketType.SETTING_UPLOAD, TicketType.SETTING_DOWNLOAD),
this::ticketEmergencyStopRequestDtoToTicket);
}
default Ticket ticketRequestDtoToTicket(TicketRequestDto dto) {
Set<TicketType> key = ticketMapStrategies.keySet().stream()
.filter(set -> set.contains(dto.getType())).findFirst()
.orElseThrow(UnsupportedTicketTypeException::new);
return ticketMapStrategies.get(key).apply(dto);
}