← Назад к вопросам

Для каких кейсов какие паттерны проектирования используются

2.0 Middle🔥 201 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерны проектирования в Frontend: кейсы применения

Как Frontend Developer, я регулярно использую различные паттерны проектирования. Рассмотрю конкретные примеры для разных ситуаций.

1. Singleton Pattern

Кейс: Единственный экземпляр класса, используется везде

class ApiClient {
  private static instance;
  private baseUrl;
  
  private constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }
  
  static getInstance() {
    if (!ApiClient.instance) {
      ApiClient.instance = new ApiClient(process.env.API_URL || '');
    }
    return ApiClient.instance;
  }
  
  async get(endpoint) {
    const response = await fetch(`${this.baseUrl}${endpoint}`);
    return response.json();
  }
}

export const api = ApiClient.getInstance();
const data = await api.get('/posts');

Где использую: API Client, Logger, Configuration Manager Плюсы: Одна точка входа Минусы: Сложнее тестировать

2. Factory Pattern

Кейс: Создание объектов разных типов по условиям

class ComponentFactory {
  static create(type, props) {
    switch (type) {
      case 'button':
        return <Button {...props} />;
      case 'link':
        return <Link {...props} />;
      case 'icon-button':
        return <IconButton {...props} />;
      default:
        throw new Error(`Unknown component type: ${type}`);
    }
  }
}

function Menu({ items }) {
  return (
    <nav>
      {items.map(item => (
        <div key={item.id}>
          {ComponentFactory.create(item.componentType, item.props)}
        </div>
      ))}
    </nav>
  );
}

Когда использую: Создание компонентов по типам Плюсы: Гибкость, централизованная логика

3. Observer Pattern (Pub/Sub)

Кейс: Реагирование на события, подписка на изменения

class EventBus {
  constructor() {
    this.listeners = new Map();
  }
  
  subscribe(event, callback) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event).push(callback);
    
    return () => {
      const callbacks = this.listeners.get(event);
      const index = callbacks.indexOf(callback);
      if (index > -1) callbacks.splice(index, 1);
    };
  }
  
  emit(event, data) {
    const callbacks = this.listeners.get(event);
    callbacks?.forEach(callback => callback(data));
  }
}

const eventBus = new EventBus();

function NotificationCenter() {
  useEffect(() => {
    const unsubscribe = eventBus.subscribe('notification:show', (message) => {
      console.log('New notification:', message);
    });
    return unsubscribe;
  }, []);
}

function savePost() {
  api.post('/posts', data).then(() => {
    eventBus.emit('notification:show', 'Post saved!');
  });
}

Где использую: Event handling, WebSocket, уведомления Плюсы: Слабая связь между компонентами

4. Strategy Pattern

Кейс: Разные алгоритмы сортировки, фильтрации

const sortByDate = {
  sort: (items) => [...items].sort((a, b) => b.date - a.date)
};

const sortByName = {
  sort: (items) => [...items].sort((a, b) => a.name.localeCompare(b.name))
};

const sortByRating = {
  sort: (items) => [...items].sort((a, b) => b.rating - a.rating)
};

function Posts() {
  const [sortStrategy, setSortStrategy] = useState(sortByDate);
  const [posts, setPosts] = useState([]);
  
  const sortedPosts = sortStrategy.sort(posts);
  
  return (
    <div>
      <select onChange={(e) => {
        const strategies = {
          date: sortByDate,
          name: sortByName,
          rating: sortByRating
        };
        setSortStrategy(strategies[e.target.value]);
      }}>
        <option value="date">By Date</option>
        <option value="name">By Name</option>
        <option value="rating">By Rating</option>
      </select>
      
      {sortedPosts.map(post => (
        <PostCard key={post.id} post={post} />
      ))}
    </div>
  );
}

Где использую: Фильтрация, сортировка, валидация

5. Adapter Pattern

Кейс: Адаптация разных источников данных к единому интерфейсу

const wordPressAdapter = (wpPost) => ({
  id: wpPost.ID,
  title: wpPost.post_title,
  author: wpPost.post_author_name,
  createdAt: wpPost.post_date
});

const mediumAdapter = (mediumPost) => ({
  id: parseInt(mediumPost.id),
  title: mediumPost.title,
  author: mediumPost.creator.name,
  createdAt: mediumPost.createdAt
});

async function getAllPosts() {
  const wpPosts = await fetchWordPress();
  const mediumPosts = await fetchMedium();
  
  return [
    ...wpPosts.map(wordPressAdapter),
    ...mediumPosts.map(mediumAdapter)
  ];
}

Где использую: Интеграция разных API, миграция

6. Decorator Pattern (HOC)

Кейс: Добавление функциональности без изменения компонента

function withAuthentication(Component) {
  return (props) => {
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    
    useEffect(() => {
      checkAuth().then(setIsAuthenticated);
    }, []);
    
    if (!isAuthenticated) {
      return <Redirect to="/login" />;
    }
    
    return <Component {...props} />;
  };
}

const ProtectedPage = withAuthentication(Dashboard);

Где использую: Auth protection, logging, analytics

7. Command Pattern

Кейс: Отмена операций (Undo/Redo)

class AddTodoCommand {
  constructor(todos, todo) {
    this.todos = todos;
    this.todo = todo;
  }
  
  execute() {
    this.todos.push(this.todo);
  }
  
  undo() {
    const index = this.todos.indexOf(this.todo);
    if (index > -1) this.todos.splice(index, 1);
  }
}

class CommandHistory {
  constructor() {
    this.history = [];
    this.currentIndex = -1;
  }
  
  execute(command) {
    command.execute();
    this.history = this.history.slice(0, this.currentIndex + 1);
    this.history.push(command);
    this.currentIndex++;
  }
  
  undo() {
    if (this.currentIndex >= 0) {
      this.history[this.currentIndex].undo();
      this.currentIndex--;
    }
  }
}

Где использую: Undo/Redo функциональность

8. Middleware Pattern

Кейс: Обработка запросов перед/после основной логики

class RequestHandler {
  constructor() {
    this.middlewares = [];
  }
  
  use(middleware) {
    this.middlewares.push(middleware);
    return this;
  }
  
  async handle(ctx) {
    let index = -1;
    
    const dispatch = async (i) => {
      if (i <= index) return;
      index = i;
      
      const middleware = this.middlewares[i];
      if (!middleware) return;
      
      await middleware(ctx, () => dispatch(i + 1));
    };
    
    await dispatch(0);
  }
}

const handler = new RequestHandler();
handler
  .use(async (ctx, next) => {
    console.log('Before');
    await next();
    console.log('After');
  })
  .use(async (ctx) => {
    ctx.body = await api.get(ctx.url);
  });

Матрица: Паттерны и их применение

ПаттернКейсПример
SingletonЕдинственный экземплярAPI Client
FactoryСоздание объектовComponent generation
ObserverРеакция на событияEvent Bus
StrategyРазные алгоритмыСортировка
AdapterУнификация интерфейсовРазные API
DecoratorДобавление функцийHOC
CommandОтмена операцийUndo/Redo
MiddlewareОбработка flowRequest handling

Когда я НЕ использую паттерны

  • Когда они усложняют, а не упрощают код
  • Когда нет реальной проблемы для их решения
  • Когда код можно решить проще

Принцип KISS (Keep It Simple, Stupid) важнее формального следования паттернам.

Итого

Паттерны проектирования — это инструменты для:

  • Организации кода
  • Упрощения тестирования
  • Масштабируемости
  • Переиспользования

Усло использовать паттерны для реальных проблем, не добавлять их просто так. В Frontend они нужны меньше чем в Backend, но критичны для больших приложений.

Для каких кейсов какие паттерны проектирования используются | PrepBro