Фабричный метод  —  это порождающий паттерн, в котором вся логика создания объектов находится в фабричном классе. В этой статье я расскажу, как применять этот паттерн в приложении Spring Boot. Итак, начнем.

Допустим, у нас есть класс Viewer, который выполняет, как следует из его названия, операции просмотра различных типов данных, таких как документы, видео, изображения и т.д. Есть также следующие модельные классы, которые в примере пусты, так как их внутренняя логика нас не интересует:

public class Document {
}

public class Image {
}

public class Video {
}

Мы, конечно, можем напрямую создавать в приложении объекты вышеуказанного класса  —  а можем, наоборот, назначить всю логику создания фабрике Viewer. Наш выбор  —  второй способ, поскольку все эти объекты реализуют общий интерфейс, и клиенту не нужно знать логику их возникновения. Также, прежде чем продолжать работу, необходимо проверить классы просмотра вышеуказанных моделей. Там, во-первых, вы увидите интерфейс Viewer, который предоставляет общие методы для всех просмоторщиков. Методы GetType и view.GetType будут необходимы для реализации фабричного шаблона, а view реализует основную логику просмотра объекта.

public interface Viewer<T> {
ViewerType getType();
void view(T object);
}

Ниже приведены классы-просмоторщики, которые реализуют Viewer.

DocumentViewer:

@Component
public class DocumentViewer implements Viewer<Document>{

@Override
public ViewerType getType() {
return ViewerType.DOCUMENT;
}

@Override
public void view(Document object) {
// кое-какая логика просмотра документов,
// реализация для нас неважна
}
}

ImageViewer:

@Component
public class ImageViewer implements Viewer<Image> {

@Override
public ViewerType getType() {
return ViewerType.IMAGE;
}

@Override
public void view(Image object) {
// кое-какая логика просмотра изображений,
// реализация для нас неважна
}
}

VideoViewer:

@Component
public class VideoViewer implements Viewer<Video> {

@Override
public ViewerType getType() {
return ViewerType.VIDEO;
}

@Override
public void view(Video object) {
// кое-какая логика просмотра видео,
// реализация для нас неважна
}
}

И перечисление ViewerType:

public enum ViewerType {
DOCUMENT,
IMAGE,
VIDEO
}

Теперь перейдем к основной части  —  к фабричному классу. Во-первых, рассмотрим карту viewerMap. В ней мы будем хранить просмоторщики, чтобы не создавать их каждый раз, когда вызывается метод getViewer для создания нужного объекта. Затем в конструкторе мы получим список классов (Spring позаботится об этом за нас), реализующих интерфейс просмотра, и поместим их на карту. Основная клиентская часть в этом классе  —  метод getViewer. Когда этот метод вызывается с требуемым типом, он сначала извлекает Viewer из карты. Если это null-объект, то мы отправляем ошибочный тип просмоторщика, означающий, что такой тип никогда не реализуется. Если такой ситуации не возникнет, то можно продолжать и вернуть просмоторщик.

@Component
public class ViewerFactory {
private static final Map<ViewerType, Viewer> viewerMap = new EnumMap<>(ViewerType.class);

@Autowired
private ViewerFactory(List<Viewer> viewers) {
for (Viewer viewer : viewers) {
viewerMap.put(viewer.getType(), viewer);
}
}

public static <T> Viewer<T> getViewer(ViewerType viewerType) {
Viewer<T> viewer = viewerMap.get(viewerType);
if (viewer == null) {
throw new IllegalArgumentException();
}
return viewer;
}
}

Дальше его можно использовать таким образом:

ViewerFactory.getViewer(ViewerType.DOCUMENT).view(new Document());

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

@Component
public class ViewerFactory {
private final Map<ViewerType, Viewer> viewerMap = new EnumMap<>(ViewerType.class);

@Autowired
private ViewerFactory(List<Viewer> viewers) {
for (Viewer viewer : viewers) {
viewerMap.put(viewer.getType(), viewer);
}
}

public <T> Viewer<T> getViewer(ViewerType viewerType) {
Viewer<T> viewer = viewerMap.get(viewerType);
if (viewer == null) {
throw new IllegalArgumentException();
}
return viewer;
}
}

Сервис для применения:

@Service
public class SomeService {
ViewerFactory viewerFactory;

@Autowired
public SomeService(ViewerFactory viewerFactory) {
this.viewerFactory = viewerFactory;
}

public void handleView() {
viewerFactory.getViewer(ViewerType.DOCUMENT).view(new Document());
}
}

Как видите, реализовать паттерн фабрики в приложениях Spring Boot  —  очень легко.

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Ömer Kurular: Spring Boot — Implementing Factory Pattern

Предыдущая статьяОсновы синтаксиса PHP
Следующая статья6 SQL-запросов, о которых должен знать каждый дата-инженер