Strategy (Стратегия) — это поведенческий паттерн проектирования, позволяющий определить семейство алгоритмов, инкапсулировать каждый из них и делать их взаимозаменяемыми. В контексте веб-компонентов на базе Lit этот паттерн становится особенно полезным для реализации вариативного поведения компонентов без дублирования кода и с минимальной связностью.
В Lit компоненты обычно наследуют от LitElement и
описывают своё состояние через свойства
(@property). Strategy паттерн позволяет вынести
динамическое поведение из компонента в отдельные классы или функции,
которые могут быть подключены к компоненту в зависимости от условий.
Пример структуры Strategy:
class BehaviorA {
execute() {
return 'Выполняется поведение A';
}
}
class BehaviorB {
execute() {
return 'Выполняется поведение B';
}
}
class BehaviorContext {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
run() {
return this.strategy.execute();
}
}
В Lit такой контекст можно интегрировать через свойства компонента, что позволяет изменять поведение на лету.
Компонент Lit может хранить ссылку на стратегию и вызывать её методы при обновлении или событиях:
import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';
class DynamicBehaviorComponent extends LitElement {
@property({ type: Object }) behavior;
static styles = css`
div {
padding: 16px;
border: 1px solid #ccc;
margin: 8px 0;
}
`;
render() {
return html`
<div>${this.behavior?.execute() ?? 'Поведение не задано'}</div>
<button @click=${() => this.changeBehavior()}>Сменить поведение</button>
`;
}
changeBehavior() {
if (this.behavior instanceof BehaviorA) {
this.beh * avior = new BehaviorB();
} else {
this.beh * avior = new BehaviorA();
}
}
}
customElements.define('dynamic-behavior', DynamicBehaviorComponent);
Здесь ключевым является то, что сам компонент не знает деталей поведения, он лишь делегирует выполнение стратегии. Это упрощает тестирование и расширение функционала.
В веб-приложениях часто требуется, чтобы один компонент мог по-разному реагировать на события или данные. Например, карточка товара может отображать цену в разных валютах, применять разные стили подсветки или изменять текстовые сообщения. Для каждого такого варианта можно создать отдельную стратегию:
class USDPriceStrategy {
constructor(price) {
this.price = price;
}
execute() {
return `$${this.price.toFixed(2)}`;
}
}
class EURPriceStrategy {
constructor(price) {
this.price = price;
}
execute() {
return `€${(this.price * 0.93).toFixed(2)}`;
}
}
Компонент может менять стратегию динамически в зависимости от пользовательских настроек или контекста:
this.beh * avior = new USDPriceStrategy(100);
this.beh * avior = new EURPriceStrategy(100);
LitElement реагирует на изменения свойств и автоматически перерисовывает компонент. Это позволяет связать Strategy паттерн с реактивным состоянием без дополнительных слушателей или ручного обновления DOM.
@property({ type: Object })
behavior;
updated(changedProperties) {
if (changedProperties.has('behavior')) {
console.log('Стратегия изменилась:', this.behavior.execute());
}
}
Таким образом, стратегия может управлять как визуальной частью компонента, так и логикой обработки данных, сохраняя компонент чистым и легко расширяемым.
class LightTheme {
apply(element) {
element.style.backgroundColor = '#fff';
element.style.color = '#000';
}
}
class DarkTheme {
apply(element) {
element.style.backgroundColor = '#222';
element.style.color = '#eee';
}
}
class ThemedComponent extends LitElement {
@property({ type: Object }) theme;
render() {
return html`<div>Тема применена</div>`;
}
updated() {
this.theme?.apply(this.shadowRoot.querySelector('div'));
}
toggleTheme() {
this.theme = this.theme instanceof LightTheme ? new DarkTheme() : new LightTheme();
}
}
customElements.define('themed-component', ThemedComponent);
Компонент делегирует всю логику изменения стиля объектам стратегий. Это упрощает добавление новых тем и делает код более чистым.
Strategy паттерн в сочетании с Lit обеспечивает мощный инструмент для создания компонентов с вариативным поведением. Использование стратегий позволяет изолировать логику, сделать код гибким и поддерживаемым, что особенно важно для сложных UI-библиотек и веб-приложений.