Ответ
Да, сталкивался с этим паттерном. Revealing Constructor позволяет создавать объекты, приватное состояние которых можно настроить только в момент создания, передав в конструктор функцию-исполнитель (executor). Это обеспечивает строгую инкапсуляцию.
Классический пример — реализация, похожая на нативный Promise:
class ImmutableLogger {
#logs = []; // Приватное поле
constructor(executor) {
// Исполнитель получает контролируемый интерфейс для работы
executor({
log: (message) => {
this.#logs.push(`${new Date().toISOString()}: ${message}`);
},
getLogs: () => [...this.#logs] // Возвращаем копию
});
}
}
// Использование:
const logger = new ImmutableLogger(({ log }) => {
log('Application started');
log('User logged in');
});
// Прямой доступ к `#logs` извне невозможен
Где это применяется:
- Встроенные API: Сам
Promiseиспользует этот паттерн — функцииresolveиrejectдоступны только исполнителю, переданному в конструктор. - Библиотеки: Некоторые библиотеки для создания безопасных или иммутабельных структур данных.
- Мои проекты: Использовал подобный подход при написании кастомного Event Emitter, где нужно было дать возможность инициализировать слушатели только один раз при создании.
Ответ 18+ 🔞
А, ну это ж классика, ёпта! С Revealing Constructor'ом сталкивался, да не раз. Паттерн тот ещё, хитрая жопа, но гениальная по своей сути. Суть в чём: ты создаёшь объект, а его приватные кишки настраиваешь только в момент рождения, через специальную функцию-исполнителя. Получается такая железобетонная инкапсуляция, что потом хоть обосрись — извне к состоянию не подлезешь.
Вот смотри, как это выглядит на практике, прям как с нативным Promise:
class ImmutableLogger {
#logs = []; // Вот это приватное поле, мой друг. Снаружи него не видно, как собаке пятая нога.
constructor(executor) {
// А тут мы этой самой функции-исполнителю даём в лапы контролируемый интерфейс
executor({
log: (message) => {
this.#logs.push(`${new Date().toISOString()}: ${message}`);
},
getLogs: () => [...this.#logs] // И копию отдаём, а не оригинал, чтоб не накосячили
});
}
}
// Используем:
const logger = new ImmutableLogger(({ log }) => {
log('Application started');
log('User logged in');
});
// И всё, приехали. Попробуй теперь до `#logs` добраться снаружи — нихуя не выйдет. Доверия к такому коду — ебать ноль, потому что его просто не сломаешь.
А где эта штука, блядь, применяется? Да везде, где нужна жёсткая защита:
- Встроенные API: Самый яркий пример —
Promise. Функцииresolveиrejectдоступны только тому, кто внутри конструктора, то есть исполнителю. Удобно, чёрт возьми. - Библиотеки: Разные умные библиотеки для безопасных или неизменяемых структур данных очень любят этот подход. Пизда рулю, когда нужно гарантировать, что состояние не испортят.
- Мои проекты: Я как-то писал кастомный Event Emitter, и там нужно было, чтобы слушатели инициализировались строго один раз при создании. Вот тут Revealing Constructor и выручил — дал возможность настроить всё изнутри, а потом наглухо захлопнул дверь. Получилось овердохуища надёжно.