Фабричный метод — это порождающий паттерн, в котором вся логика создания объектов находится в фабричном классе. В этой статье я расскажу, как применять этот паттерн в приложении 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 — очень легко.
Читайте также:
- Кэширование в связке Spring Boot + Redis + PostgreSQL
- Генерируем образы Docker с помощью Spring Boot
- JWT-аутентификация в Spring Boot Webflux
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Ömer Kurular: Spring Boot — Implementing Factory Pattern