Spring Boot: реализация фабричного метода

Spring Boot: реализация фабричного метода

Java библиотека

Фабричный метод — это порождающий паттерн, в котором вся логика создания объектов находится в фабричном классе. В этой статье я расскажу, как применять этот паттерн в приложении 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 — очень легко.

Источник


Report Page