'How to Autowired multiple interface implementations and use Map and Enum as a key

For example, there is a task, without violating the Open/Closed principle, to safely add new implementations for sending messages in different ways. The input comes with a parameter that contains the type of "transport" for sending messages or the device where the messages will arrive.

As an input parameter, you need to use Enum, but design the system so that you do not use switch when the parameter comes as a string, but immediately call the desired service in accordance with the stated conditions.

Here is my implementation.

  • Test
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class NotificationMasterTest extends MultiImplementationInterfacesAndEnumApplicationTests {

    @Autowired
    private NotificationMaster notification;

    @Test
    void send() {

        String phone = "phone";

        TypeCommunication typeCommunication =
                TypeCommunication.valueOf("phone");//Runtime Exception

        notification.send(TypeCommunication.PHONE);
    }
}
  • enum
public enum TypeCommunication {

    PHONE("phone"),
    EMAIL("email"),
    KAFKA("kafka");

    private String value;

    TypeCommunication(String value) {
        this.value = value;
    }


    public String getType() {
        return this.value;
    }
}
  • interface
public interface Notification {

    void sendNotice();

    TypeCommunication getType();
}
  • PhoneImplementation
@Service
public class NotificationPhoneImpl implements Notification {

    private final TypeCommunication typeCommunication = TypeCommunication.PHONE;

    public NotificationPhoneImpl() {
    }

    @Override
    public void sendNotice() {
        System.out.println("Send through --- phone---");
    }

    @Override
    public TypeCommunication getType() {
        return this.typeCommunication;
    }
}
  • EmailEmplementation
@Service
public class NotificationEmailImpl implements Notification {

    private final TypeCommunication typeCommunication = TypeCommunication.EMAIL;

    @Override
    public void sendNotice() {
        System.out.println("Send through --- email ---");
    }

    @Override
    public TypeCommunication getType() {
        return this.typeCommunication;
    }
}
  • master
package com.example.multi.implementation.interfaces.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Component
public class NotificationMaster {

    private  Map<TypeCommunication, Notification> notificationService = new HashMap<>();

    private Set<Notification> notification;

    public NotificationMaster(Set<Notification> notification) {
        this.notification = notification;
    }

    @PostConstruct
    private void init(){
        notification.stream()
                .forEach(service -> {
                    TypeCommunication type = service.getType();
                    notificationService.put(type, service);
                });
    }

    public void send(TypeCommunication typeCommunication) {
        Notification notification = notificationService.get(typeCommunication);
        notification.sendNotice();
    }
}

I can't figure out how to pass a string and convert it to Enum on the fly (without using switch) and immediately get the desired implementation.

Maybe there is a more flexible solution in which Spring would have already prepared the components without me, so that I don't use Postconstruct and manually create a map with different service implementations?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source