PassFront
  1. PassFront
  2. База знаний
  3. Смежные принципы проектирования

Что такое принцип «Tell, Don't Ask»?

Смежные принципы проектирования

Tell, Don't Ask: вместо того чтобы запрашивать у объекта состояние и решать снаружи, объекту говорят, что сделать, — а решение он принимает сам, на своих данных.

Ask

if (account.getBalance() >= amount && !account.isFrozen()) {
  account.setBalance(account.getBalance() - amount);
}

Правило списания размазано по вызывающему коду: в пяти местах его скопируют по-разному, и одно забудет проверку заморозки.

Tell

class Account {
  #balance;
  #frozen;
  constructor(balance, frozen = false) {
    this.#balance = balance;
    this.#frozen = frozen;
  }
  withdraw(amount) {
    if (this.#frozen) throw new Error('Счёт заморожен');
    if (amount > this.#balance) throw new Error('Недостаточно средств');
    this.#balance -= amount;
  }
}

Инвариант «баланс не уходит в минус» проверяется в одном месте, снаружи его не обойти.

Что даёт

  • Настоящую инкапсуляцию: поведение вместо пары геттер-сеттер на каждое поле.
  • Защиту от анемичной модели (термин Фаулера): логика живёт рядом с данными.
  • Меньше зацепления: вызывающему не нужно устройство объекта.

Когда спрашивать нормально

  • Для отображения: UI читает баланс, чтобы его показать.
  • CQS Мейера: методы-запросы без побочных эффектов законны; плохо, когда по их результату за объект принимают решение.
  • В Redux данные намеренно отделены от поведения: компонент отправляет действие withdraw, а не вычисляет новый баланс сам.

Коротко для собеседования

Решение, зависящее от состояния объекта, принимает сам объект: вызывающий говорит «спиши деньги», а не читает баланс и меняет его сам. Так инвариант защищён в одном месте; читать состояние для отображения нормально, а операции над несколькими агрегатами уходят в доменный сервис.

Другие вопросы раздела