Привет всем! Я Мыльников Кирилл, frontend-разработчик в компании Usetech. Сегодня хочу поделиться примером реализации фабричного метода во фронтенде и объяснить, когда и где его следует применять. Освежим память о паттернах и роли, которую они играют в проектах.
Я готовлю серию статей о паттернах в React. Первая статья будет посвящена фабричному методу. Все примеры мы с вами разберем на реальных сценариях, с которыми вы могли бы столкнуться в проекте.
Давайте по классике начнем с определений, вспомним, что такое паттерны и фабричный метод.
Что такое паттерн?
Паттерн – это часто встречающееся решение определенной проблемы при проектировании архитектуры программы. В отличие от готовых функций или библиотек, паттерн нельзя просто взять и скопировать в программу. Паттерн представляет собой общую концепцию решения той или иной проблемы, которую в конечном итоге нужно подстроить под ваш код.
Что такое «Фабричный метод»?
Фабричный метод – это порождающий паттерн проектирования, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
Итак, мы разобрали классическую трактовку паттернов и фабричного метода, которые традиционно описываются в книгах и официальных источниках.
Давайте теперь все это адаптируем под наш проект на React.
Фабричный метод в React
В React фабричный метод служит для динамического создания компонентов или функций с различными свойствами или поведениями. Представьте фабрику, где товары создаются по заказу с уникальными параметрами: размером, цветом и материалом. Здесь процесс создания товаров автоматизирован, что позволяет изготавливать предметы без необходимости их ручной настройки. Также и в React мы можем создать фабричную функцию для компонентов.
Так давайте же перейдем от теории к практике. Все примеры буду показывать в песочнице, сначала буду описывать проблему, которая может возникнуть в проекте, а потом сразу покажу решение через фабричный метод.
Реализация фабричного метода в React
Будем использовать для тестовых данных вот этот сервис DummyJSON.
Вот ссылка на codesandbox с примером. В первом случае список отображается там, где могут возникнуть проблемы при внедрении или изменении логики в дальнейшем, а во втором – применен паттерн «Фабричный метод». Мы получаем список продуктов и выводим их, но при различных категориях нам приходится настраивать каждую карточку индивидуально.
Компонент становится сложным и трудночитаемым из-за условий, и изменения могут нарушить логику других категорий, но это можно исправить с помощью паттерна «Фабричный метод».
Пример
const Product = ({ product }: IProductProps) => {
return (
<li>
<div className={styles.product}>
<img src={product.thumbnail} alt="thumbnail" />
</div>
<div className={styles.productInfo}>
{product.category !== "beauty" && (
<p className={styles.productTitle}>{product.title}</p>
)}
<p className={styles.productPrice}>${product.price}</p>
</div>
{product.category === "groceries" && (
<p className={styles.description}>{product.description}</p>
)}
{product.category === "fragrances" && (
<p className={styles.policy}>
<span className={styles.text}> Условия возврата: </span>
{product.returnPolicy}
</p>
)}
</li>
);
};
export default Product;
Вот наша вторая реализация, где мы применяем паттерн «Фабричный метод». Это позволяет нам легко вносить изменения и настраивать каждую карточку в зависимости от категории. Теперь мы можем без проблем изменять логику для каждого типа продукта, и никакие изменения не затронут другие категории. Не забываем, что можно перейти по ссылке и посмотреть пример полностью – codesandbox.
Пример
import { IProducts } from "../../../../typings";
import BeatuProduct from "./BeautyProduct";
import GroceriesProduct from "./GroceriesProduct";
import FragrancesProduct from "./FragrancesProduct";
interface IProductFactoryProps {
product: IProducts;
}
const ProductFactory = ({ product }: IProductFactoryProps) => {
switch (product.category) {
case "beauty":
return <BeautyProduct product={product} />;
case "groceries":
return <GroceriesProduct product={product} />;
case "fragrances":
return <FragrancesProduct product={product} />;
default:
return null;
}
};
export default ProductFactory;
Еще один наглядный пример использования фабричного метода это UI – компоненты, допустим у нас в проекте есть кнопка, которая может выглядеть по-разному в различных частях интерфейса – с разными цветами и темами. В рассмотренном примере ниже мы видим, что в зависимости от типа кнопки мы отображаем конкретный компонент кнопки со своими стилями и свойствами. Благодаря использованию паттерна «Фабричный метод», нам легко и просто настраивать кнопку под нужды проекта и в любой момент заменять ее другой кнопкой с новым типом или добавить новый тип и настроить через новый компонент, реализация тут – codesanbox.
Пример
import DangerButton from "./DangerButton";
import PrimaryButton from "./PrimaryButton";
export interface IButtonProps {
type: "primary" | "danger";
title: string;
onClick?: () => void;
}
const Button = ({ type, title, onClick }: IButtonProps) => {
switch (type) {
case "primary":
return <PrimaryButton title={title} onClick={onClick} />;
case "danger":
return <DangerButton title={title} onClick={onClick} />;
default:
return null;
}
};
export default Button;
Паттерны, включая «Фабричный метод» помогают нам писать более гибкий и предсказуемый код, который легко поддается изменениям и расширению. Применение паттернов важно для создания высококачественного и хорошо структурированного программного обеспечения.
Материалы
Паттерны – Каталог паттернов проектирования (refactoringguru.cn)
Codesanbox – ProductFactory.tsx – sandbox – CodeSandbox