Избегайте длинных списков зависимостей в Spring Boot - PullRequest
1 голос
/ 08 мая 2020

Я работаю над кодом, который имеет постоянно увеличивающееся количество реализаций интерфейса VendorService. Прямо сейчас, когда эти службы используются, мы автоматически подключаем их все в конструкторе, что приводит к длинным спискам зависимостей. Есть ли предпочтительный способ обработки зависимостей при многократном использовании одного интерфейса?

Текущий подход:

private final VendorService xVendorService;
private final VendorService yVendorService;
private final VendorService zVendorService;
...

@Autowired
public VendorDelegateService(XVendorService xVendorService, 
                             YVendorService yVendorService, 
                             ZVendorService zVendorService, 
                             ...) {
    this.xVendorService = xVendorService;
    this.yVendorService = yVendorService;
    this.yVendorService = yVendorService;
    ...
}

public void doSomething(VendorId vendorId) {
    if (vendorId = VendorId.X) {
        xVendorService.doSomething();
    } else if (vendorId = VendorId.Y) {
        yVendorService.doSomething();
    } else if (vendorId = VendorId.Z) {
        zVendorService.doSomething();
    } 
    ...
}

Очевидно, что это очень многословно и требует обновления всякий раз, когда создается новая реализация интерфейса.

Альтернативой является получение Bean из ApplicationContext, что-то вроде:

private final ApplicationContext context;

@Autowired
public VendorDelegateService(ApplicationContext context) {
    this.context = context;
}

public void doSomething(VendorId vendorId) {
    context.getBean(VendorService.class, vendorId.name()).doSomething();
}

Для этого не потребуется еще одна скобка if / else с каждой новой реализацией, но это тупо и не кажется правильным. Этот лог c, конечно, может быть выделен в отдельный класс, чтобы уменьшить эту проблему. Есть ли другие подходы, которые я не рассматривал?

1 Ответ

4 голосов
/ 09 мая 2020

Я считаю, что это вопрос предпочтения, существует ли идиоматический c способ для этого, но я предлагаю следующее решение:

Создайте интерфейс для всех служб, мы можем назвать это VendorService:

public interface VendorService {
    void doSomething();
    VendorId getVendorId();
}

Теперь мы хотели бы реализовать этот интерфейс для всех служб, в качестве примера это можно сделать для XVendorService:

@Service
public XVendorService implements VendorService {
    private VendorId vendorId = ....    

    @Override
    public void doSomething() {
        ...
    }

    @Override
    public VendorId getKey() {
        return vendorId;
    }
}

Теперь для VendorDelegateService мы можем сделать что-то вроде этого:

@Service
public class VendorDelegateService {
    private Map<VendorId, VendorService> services = new HashMap<>();

    @Autowired
    public AllServices(Set<? extends VendorService> serviceSet) {
        serviceSet.stream().forEach(service -> services.put(service.getVendorId(), service));
    }

    public void doSomething(VendorId vendorId) {
        if (services.containsKey(vendorId)) {
            services.get(vendorId).doSomething();
        }
    }
}

Обратите внимание, что с Set<? extends VendorService> serviceSet все службы будут автоматически подключены. Создав карту впоследствии, мы можем отправить наш запрос каждой службе на основе ее vendorKey.

...