Adapter vs Decorator vs Proxy vs Facade — Сравнение
Четыре структурных паттерна, которые в JS выглядят почти одинаково. Разница — в интенции, не в коде.
Главный тезис автора
«В JavaScript этим паттернам становятся очень похожи. В динамических языках они становятся иногда по коду совершенно неотличимы. Но они существенно отличимы по цели, по интенции своего существования.»
В C++/Java они различались по реализации (наследование, контракты, mixin). В JS — структурная композиция везде одинаковая.
Различающая таблица
| Паттерн | Сколько абстракций | Зачем |
|---|---|---|
| Adapter | 1 → 1 | Стыковать две существующие абстракции с несовместимыми контрактами |
| Decorator | 1 → 1 | Добавить метаданные/поведение к одной абстракции, сохраняя её интерфейс |
| Proxy | 1 → 1 | Перехватить доступ для контроля, защиты, ленивых вычислений, профилирования |
| Facade | N → 1 | Упростить доступ к сложной подсистеме из нескольких классов |
| Composite | 1 → дерево | Единый интерфейс к дереву похожих узлов |
| Flyweight | N эталонов + N инстансов | Экономия памяти через общее состояние |
| Bridge | 2 иерархии | Разделить иерархии наследования, чтобы менять независимо |
По коду они почти одинаковы
// Adapter
class FooAdapter {
constructor(legacyFoo) { this.foo = legacyFoo; }
newMethod { return this.foo.oldMethod; }
}
// Decorator
class LoggedFoo {
constructor(foo) { this.foo = foo; }
method(x) {
console.log('call', x);
return this.foo.method(x);
}
}
// Proxy (классическая реализация — без new Proxy)
class FooProxy {
constructor(foo) { this.foo = foo; }
method(x) {
if (!this.hasAccess) throw new Error('forbidden');
return this.foo.method(x);
}
}
// Facade
class FooFacade {
#db; #cache; #logger;
constructor { /* инициализация подсистемы */ }
doStuff(x) { /* координирует #db, #cache, #logger */ }
}
Структурно — везде поле + делегация. Но зачем мы это делаем, разное:
- Adapter: «контракты не совпадают»
- Decorator: «нужны метаданные/побочное поведение»
- Proxy: «нужен контроль доступа»
- Facade: «много классов, нужен простой API»
Что часто путают
Adapter vs Decorator
- Adapter изменяет интерфейс:
legacyFoo.oldMethod→adapter.newMethod - Decorator сохраняет интерфейс:
foo.method→decorated.method(только добавляет поведение)
Decorator vs Proxy
- Decorator добавляет поведение/метаданные
- Proxy контролирует доступ — может запретить вызов
Proxy vs Flyweight
- Proxy перехватывает доступ (контроль)
- Flyweight экономит память (производительность)
Facade vs Adapter
- Adapter: 1 контракт → другой контракт (1:1)
- Facade: много контрактов → 1 контракт (N:1)
Facade vs Composite
- Composite — дерево однотипных узлов с общим интерфейсом
- Facade — несвязанные классы за одним интерфейсом
- Корень дерева Composite можно назвать Facade'ом
Bridge vs Mediator
- Bridge знает об иерархиях наследования и связывает их по контракту
- Mediator не знает об иерархиях, централизует связи между объектами
Главные тезисы автора
- «В JS хватает обычной структурной композиции» — поэтому паттерны схлопываются.
- «Структурные паттерны — это композиция классов»: ссылка на другой инстанс внутри.
- Все можно реализовать в функциональном стиле через замыкание.
- Различать паттерны по интенции, не по коду — главное правило.
- Adapter = стыковка двух абстракций, которые не можем/не хотим переписать.
- Decorator = добавление декларативного синтаксиса + метаданных.
- Proxy в JS = встроенный механизм перехвата, чего классы не могут.
- Facade = главный паттерн архитектурных границ.
🎓 Источники
- 🎓 Адаптер, Декоратор, Прокси, Фасад — В чём разница · 2026-02-09
- Главный источник этой темы
- Различение по интенции, не по коду
- Авторские определения vs книжные
- 🎓 GoF Patterns Обзор всех паттернов · 2025-04-29
- Композиция во всех структурных паттернах
- Facade vs Adapter
- Bridge vs Mediator