Абстрактный шаблон фабрики @Service в SpringBoot - PullRequest
3 голосов
/ 30 сентября 2019

Мне интересно, как использовать класс реализации Factory Factory в качестве @Service внутри моего приложения. У меня есть несколько провайдеров желаемой логики, и я вводил их через конструктор. Проблема в том, что я не уверен, должен ли мой Фабрика иметь конструктор, и если он должен быть, должен ли он быть частным.

Я аннотировал этот класс как @Service, но мне было интересно, если что-то из этого хорошо икаковы лучшие практики. Спасибо и извините, если верхний беспорядок, это на самом деле мой первый вопрос и сообщение.

Вот мой фрагмент кода:

    private final FirstClient first;
    private final SecondClient second;

    private SentimentFactoryImpl(FirstClient first, SecondCliend second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public SentimentClient get(String clientName) {
        if ("First".equalsIgnoreCase(clientName)) {
            return this.first;
        } else if ("Second".equalsIgnoreCase(clientName)) {
            return this.second;
        } 
        throw new UnknownClientException("Service doesn't provide support for client: " + clientName);
    }

Ответы [ 4 ]

2 голосов
/ 30 сентября 2019

На самом деле ядро ​​Spring Framework представляет собой сверхгибкую и мощную абстрактную фабрику. Если вы используете Spring, вы должны делегировать все функциональные возможности создания классов ядру Spring. В вашем случае я предпочитаю использовать Spring java Configurations для создания классов. Эквивалент вашей фабрики в Spring будет выглядеть следующим образом.

@Configuration
public class ServiceConfig {

    @Bean
    public SentimentClient firstClient() {
        return new FirstClient();
    }

    @Bean
    public SentimentClient secondClient() {
        return new SecondClient();
    }
}

После того, как этот класс был распознан ядром Spring, он будет использовать его как фабрику для получения экземпляра службы каждый раз, когда потребуется любой другой компонент Spring. Это. Вам не нужно вызывать какой-либо метод get, как на вашей фабрике. Spring делает это автоматически.

@Service
public class SomeService {

    private final SentimentClient firstClient;

    @Autowired
    private SomeService(SentimentClient firstClient) {
        this.firstClient = firstClient;
    }

    // Some business logic here 
}

Стоит отметить, что имя фабричного метода в конфигурации Spring - это имя компонента (службы), созданного этим методом, и это имя с типом класса используется длянайти нужный компонент для автопроводки.

1 голос
/ 30 сентября 2019

Для применения передовых методов вы должны рассмотреть следующие методы:

  1. Не связывайте тесно поля полей объекта, а вставляйте их. Это позволит изменять класс поля объекта по мере необходимости.

  2. Для выполнения 1-го пункта всегда работайте по контракту, т.е. используйте интерфейсы.

  3. Решите, хотите ли вы синглтоны или новый объект каждый раз, когда вы вызываете фабрику.

  4. Ремонтопригодность абстрактной фабрики.

В вашем случае вы уже придерживались # 2. Для остальных: a) создайте bean-компоненты для FirstClient и SecondClient.

b) Если они должны быть одноэлементными, как видно из конечного ключевого слова, но не очевидны для фабричного класса, сделайте оба клиентских класса одинарными. *

в) Вместо строки clientName создайте перечисление, так как оно более удобное в обслуживании и безопасном, так как вы всегда ищете хорошо определенные значения.

d) Избавьтесь от if-else и лучше используйте Карту, которая заполняется как постконструкция вашей фабрики. Это ускорит получение бобов.

e) Вы также можете подумать о ленивой инициализации:).

class SentimentFactoryImpl{
    private Map<Client, SentimentClient> factoryMap; 
    @Autowired
    private final FirstClient first;
    @Autowired
    private final SecondClient second;



    @Override
    public SentimentClient get(String clientName) {
       Client Client.getByName(clientName);
     if(!factoryMap.contains(client)){
         throw new UnknownClientException("Service doesn't provide support for client: " 
         + clientName);
      }
       return factoryMap.get(Clients.getByName(clientName));
    }
0 голосов
/ 06 октября 2019

Я знаю, что вы приняли ответ, но я просто хочу высказать свое собственное мнение. Во-первых, я не думаю, что вы хотите использовать шаблон Abstract Factory, кажется, что вы не создаете объекты, а просто хотите найти клиентские объекты на основе их имени. Это работа для Locator, давайте назовем его ClientLocator (из-за отсутствия лучшего названия).

Идея состоит в том, что вы создаете класс с именем ClientLocator, который реализует ApplicationContextAware. Реализуя этот интерфейс, Spring вводит объект ApplicationContext через метод установки. Используя объект ApplicationContext, мы можем искать bean-компонент по его имени, после чего мы можем проверить тип bean-компонента, чтобы убедиться, что мы получили правильный, и выдать ошибку, когда мы этого не сделаем.

Вот как выглядит код.

Клиенты

// SentimentClient.java
public interface SentimentClient {

    void call();

}


// FirstClient.java
@Service
public class FirstClient implements SentimentClient {


    @Override
    public void call() {
        System.out.println("Hello from the first client");
    }
}

// SecondClient.java
@Service
public class SecondClient implements SentimentClient {

    @Override
    public void call() {
        System.out.println("Hello from the second client");
    }
}


ClientLocator.java


@Component
public class ClientLocator implements ApplicationContextAware {


    private ApplicationContext ctx;

    public SentimentClient get(String name) {
        return ctx.getBean(name + "Client", SentimentClient.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        this.ctx = ctx;
    }
}

Вот как мыможно использовать локатор.

ClientLocatorDemo.java

@Component
public class ClientLocatorDemo implements CommandLineRunner {

    @Autowired
    private ClientLocator clientLocator;

    @Override
    public void run(String... args) {
        SentimentClient firstClient = clientLocator.get("first");
        SentimentClient secondClient = clientLocator.get("second");

        firstClient.call();
        secondClient.call();

        // output:
        // Hello from the first client
        // Hello from the second client
    }
}


Таким образом, каждый раз, когда код хочет получить клиента по его имени, просто подключите1028 * и вызовите метод get(String name).

И самое главное, вы можете создать реализацию клиента столько, сколько захотите, и получить их по имени (при условии, что у каждого есть уникальное имя и заканчивается на «Клиент») без необходимости автоматически связывать их по очереди. .

0 голосов
/ 30 сентября 2019

Поскольку вы говорите, что этот класс аннотирован как @Service, я предполагаю, что вы хотите, чтобы оба клиента были автоматически подключены к нему, и что они оба также являются компонентами.

В этом случае вы можетелибо автоматически связать их в конструкторе (в этом случае конструктор должен быть открытым). Кроме того, вы можете обойтись без конструктора и автоматически связать поля напрямую (или с помощью методов установки) - это менее рекомендуется, лучше использовать автоматическое связывание с помощью конструктора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...