Какие знаешь принципы ООП

Ответ

В контексте JavaScript и Node.js, который поддерживает ООП через классы (синтаксический сахар над прототипами), я применяю четыре основных принципа:

  1. Инкапсуляция: Сокрытие внутреннего состояния объекта и предоставление контролируемого интерфейса. В современных 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}`);
      }
    }
  2. Наследование: Создание нового класса на основе существующего для повторного использования кода.

    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); // Использование метода родителя
        // ... специфичная логика
      }
    }
  3. Полиморфизм: Возможность объектов с одинаковым интерфейсом иметь разную реализацию. Часто используется с наследованием или интерфейсами.

    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');
  4. Абстракция: Моделирование ключевых характеристик сущности, скрывая сложные детали реализации.

    // Абстракция "Отправка уведомления"
    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 { /* ... */ }

Вот и вся философия. Кажется, что нихуя не нужно, пока не начнёшь делать большой проект. А потом понимаешь, что без этого — просто ад, чувак. Код превращается в спагетти, где всё зависит от всего, и поправить одну строчку — это полпроекта переписать. Так что учи, применяй, не будь мартышлюшкой.

Видео-ответы