Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Що таке «одержимість примітивами» і як об'єкти-значення її лікують?

Одержимість примітивами (Primitive Obsession) - доменні поняття представлені «голими» рядками, числами й масивами: email - string, гроші - float, телефон - string, діапазон дат - два окремі DateTime.

function transfer(string $fromIban, string $toIban, float $amount, string $currency): void

Проблеми:

  • валідація розкидана - кожне місце, що приймає IBAN, мусить перевіряти формат (і одне колись забуде);
  • легко переплутати аргументи однакового типу ($toIban, $fromIban) - компілятор не допоможе;
  • неявні правила (гроші не можна додавати в різних валютах, сума не від'ємна) повторюються чи пропускаються;
  • поведінка розповзається по допоміжних функціях (formatPhone(), normalizeEmail()).

Ліки - об'єкт-значення (Value Object):

final readonly class Money
{
    private function __construct(public int $cents, public Currency $currency) {}

    public static function of(int $cents, Currency $currency): self
    {
        if ($cents < 0) {
            throw new InvalidArgumentException('Сума не може бути від\'ємною');
        }
        return new self($cents, $currency);
    }

    public function add(self $other): self
    {
        if ($other->currency !== $this->currency) {
            throw new CurrencyMismatch();
        }
        return new self($this->cents + $other->cents, $this->currency);
    }

    public function equals(self $other): bool
    {
        return $this->cents === $other->cents && $this->currency === $other->currency;
    }
}

function transfer(Iban $from, Iban $to, Money $amount): void

Властивості об'єкта-значення:

  • валідний за побудовою: створити некоректний Iban неможливо - перевірка в одному місці;
  • незмінний (readonly): операції повертають новий об'єкт;
  • рівність за значенням, а не за посиланням;
  • поведінка поруч з даними: $money->add(), $email->domain(), $phone->formatted().

У Laravel: власні касти (CastsAttributes) перетворюють колонки бази на об'єкти-значення й назад:

protected function casts(): array
{
    return ['price' => MoneyCast::class, 'email' => EmailCast::class];
}

Енуми PHP - теж ліки від одержимості примітивами для фіксованих наборів значень (статуси, ролі, валюти).

Де зупинитися: не кожен рядок потребує класу. Об'єкт-значення окупається, коли з поняттям пов'язані правила (валідація, операції, форматування) і воно передається між шарами застосунку.

Докладніше в документації: Refactoring.Guru: Primitive Obsession

1

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання