Реализовать сервис отправляющий сообщения через внешние библиотеки с возможностью добавления новые уведомлений

7. Реализовать расширяемый сервис отправки уведомлений

Условие задачи:
Необходимо реализовать сервис Notificator, который отправляет сообщение через несколько внешних сервисов, например Email и SMS.

При добавлении нового канала уведомлений код Notificator не должен изменяться.

Код:

public class EmailNotificationService {

    public void sendEmail(String message) {
        System.out.println("Send from email service");
        System.out.println(message);
    }
}

public class SmsNotificationService {

    public void sendSms(String message) {
        System.out.println("Send from sms service");
        System.out.println(message);
    }
}

Спойлеры к решению

Подсказки
💡 Введи общий интерфейс для всех каналов уведомлений.
💡 Внешние библиотеки можно обернуть адаптерами.
💡 Spring умеет внедрять сразу List всех бинов одного интерфейса.
💡 Внешние сервисы также нужно зарегистрировать как Spring-бины.
💡 Для добавления нового канала достаточно создать новую реализацию интерфейса.

Решение

Общий интерфейс:

public interface NotificationService {

    void send(String message);
}

Адаптер для Email:

@Service
public class EmailNotificationAdapter
        implements NotificationService {

    private final EmailNotificationService emailService;

    public EmailNotificationAdapter(
            EmailNotificationService emailService
    ) {
        this.emailService = emailService;
    }

    @Override
    public void send(String message) {
        emailService.sendEmail(message);
    }
}

Адаптер для SMS:

@Service
public class SmsNotificationAdapter
        implements NotificationService {

    private final SmsNotificationService smsService;

    public SmsNotificationAdapter(
            SmsNotificationService smsService
    ) {
        this.smsService = smsService;
    }

    @Override
    public void send(String message) {
        smsService.sendSms(message);
    }
}

Регистрация классов внешних библиотек:

@Configuration
public class NotificationConfiguration {

    @Bean
    public EmailNotificationService emailNotificationService() {
        return new EmailNotificationService();
    }

    @Bean
    public SmsNotificationService smsNotificationService() {
        return new SmsNotificationService();
    }
}

Сервис отправки уведомлений:

@Service
public class Notificator {

    private final List<NotificationService> channels;

    public Notificator(
            List<NotificationService> channels
    ) {
        this.channels = channels;
    }

    public void notifyAll(String message) {
        for (NotificationService channel : channels) {
            channel.send(message);
        }
    }
}

Spring автоматически передаст в List<NotificationService> все зарегистрированные реализации интерфейса.

Чтобы добавить новый канал, например Telegram, достаточно создать новый адаптер:

@Service
public class TelegramNotificationAdapter
        implements NotificationService {

    @Override
    public void send(String message) {
        // отправка сообщения
    }
}

Изменять Notificator при этом не потребуется.

Здесь используется паттерн Adapter для приведения разных внешних API к общему интерфейсу и соблюдается принцип Open/Closed: систему можно расширять новыми каналами без изменения основной логики.