Ответ
В контексте JavaScript и Node.js, который поддерживает ООП через классы (синтаксический сахар над прототипами), я применяю четыре основных принципа:
-
Инкапсуляция: Сокрытие внутреннего состояния объекта и предоставление контролируемого интерфейса. В современных JS/Node.js это реализуется через приватные поля (
#) и геттеры/сеттеры.class Logger { #logLevel = 'INFO'; // Приватное поле set logLevel(level) { if (['DEBUG', 'INFO', 'ERROR'].includes(level)) { this.#logLevel = level; } } log(message) { console.log(`[${this.#logLevel}] ${message}`); } } -
Наследование: Создание нового класса на основе существующего для повторного использования кода.
class BaseService { constructor(dbClient) { this.db = dbClient; } validate() { /* общая логика */ } } class UserService extends BaseService { constructor(dbClient, emailService) { super(dbClient); // Вызов конструктора родителя this.emailService = emailService; } async createUser(data) { this.validate(data); // Использование метода родителя // ... специфичная логика } } -
Полиморфизм: Возможность объектов с одинаковым интерфейсом иметь разную реализацию. Часто используется с наследованием или интерфейсами.
class CacheProvider { get(key) { throw new Error('Not implemented'); } } class RedisCache extends CacheProvider { get(key) { /* логика получения из Redis */ } } class MemoryCache extends CacheProvider { get(key) { /* логика получения из памяти */ } } // Один интерфейс, разные реализации const cache = process.env.USE_REDIS ? new RedisCache() : new MemoryCache(); const data = await cache.get('user:123'); -
Абстракция: Моделирование ключевых характеристик сущности, скрывая сложные детали реализации.
// Абстракция "Отправка уведомления" class NotificationSender { send(user, message) { // Абстрактный метод, детали в подклассах throw new Error('Method send() must be implemented'); } } // Конкретные реализации скрывают детали (SMTP, API push-сервиса и т.д.) class EmailSender extends NotificationSender { /* ... */ } class PushSender extends NotificationSender { /* ... */ }
Ответ 18+ 🔞
А, ну вот, опять про эти ваши принципы ООП, ебать мои старые костыли! Сидишь такой, пишешь код, а тут тебе — инкапсуляция, наследование, полиморфизм, абстракция. Как будто на дворе 2002-й год и мы только про Java узнали. Но, хуй с горы, в Node.js это тоже есть, и если не понимать, какого хуя оно нужно, можно такой пиздопроебибны наворотить, что потом сам от себя охуеешь.
Ну смотри, первый принцип — Инкапсуляция. Это когда ты свою внутреннюю кухню прячешь, а наружу торчит только красивая вывеска. В JS раньше было нихуя не спрятать, все поля как на ладони. А сейчас, ёпта, есть приватные поля с решёткой (#). Вот смотри, какой хитрая жопа:
class Logger {
#logLevel = 'INFO'; // Приватное поле, спрятано, как золото в сейфе
set logLevel(level) {
if (['DEBUG', 'INFO', 'ERROR'].includes(level)) {
this.#logLevel = level; // Контролируемый доступ, а не просто `logLevel = 'БАЛБЕС'`
}
}
log(message) {
console.log(`[${this.#logLevel}] ${message}`); // А тут уже можно
}
}
Суть в чём? Чтобы какой-нибудь распиздяй в другом месте не взял и не присвоил logger.logLevel = 'ПИЗДЕЦ'. Доверия ебать ноль, поэтому всё через геттер/сеттер. Красота.
Дальше — Наследование. Это когда ты не хочешь писать одно и то же по сто раз, как дурак. Берёшь готовый класс и говоришь: «Всё твоё — теперь моё, чувак». В JS это через extends и super.
class BaseService {
constructor(dbClient) {
this.db = dbClient; // Общая штука для всех сервисов
}
validate() { /* общая логика валидации, чтоб не копипастить */ }
}
class UserService extends BaseService {
constructor(dbClient, emailService) {
super(dbClient); // Вызываешь конструктор родителя, он тебе базу настроит
this.emailService = emailService; // А тут уже своё, специфичное
}
async createUser(data) {
this.validate(data); // Метод-то от родителя взял, бесплатно!
// ... а тут уже своя, пользовательская, логика
}
}
Получается экономия, не надо изобретать велосипед. Удобно, ёпта.
Третий — Полиморфизм. Звучит сложно, а на деле — «один интерфейс, много реализаций». Как розетка: в неё можно и чайник, и зарядку воткнуть, а розетке похуй, что там. Главное — чтобы вилка подходила.
class CacheProvider {
get(key) { throw new Error('Not implemented'); } // Говорим: "Дети, реализуйте сами!"
}
class RedisCache extends CacheProvider {
get(key) { /* тут сложная логика получения из Redis */ }
}
class MemoryCache extends CacheProvider {
get(key) { /* а тут просто из оперативки берём */ }
}
// И вот магия: одной строкой меняем всю реализацию кеша
const cache = process.env.USE_REDIS ? new RedisCache() : new MemoryCache();
const data = await cache.get('user:123'); // Вызываем одинаково, а работает по-разному!
Вот это и есть полиморфизм. Один метод get, а под капотом — овердохуища разных действий.
Ну и наконец, Абстракция. Это когда ты работаешь с чем-то сложным, как с простой штукой. Тебе не нужно знать, как там двигатель в машине устроен, чтобы газ давить. Ты просто знаешь про педаль газа и руль.
// Абстракция "Отправка уведомления". Нам главное — отправить.
class NotificationSender {
send(user, message) {
// А как именно — это уже проблемы детей этого класса
throw new Error('Method send() must be implemented');
}
}
// Конкретные реализации. Они скрывают всю свою грязную работу.
class EmailSender extends NotificationSender {
send(user, message) {
// А тут тебе и SMTP, и заголовки, и кодировки — целая ёперный театр!
// Но тому, кто вызывает `sender.send()`, на это похуй.
}
}
class PushSender extends NotificationSender { /* ... */ }
Вот и вся философия. Кажется, что нихуя не нужно, пока не начнёшь делать большой проект. А потом понимаешь, что без этого — просто ад, чувак. Код превращается в спагетти, где всё зависит от всего, и поправить одну строчку — это полпроекта переписать. Так что учи, применяй, не будь мартышлюшкой.
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶