[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Value Object
Arh
Отсюда.

А зачем final?
А если кому то понадобиться делать дополнительные действия или изменить существующую логику, надо будет наследоваться?

Даже если без final. Как наследоваться и подменить в системе этот класс на наследника, ведь там куча new монолитных.
Допустим new Username($someService, $username); это зависимость какого нибудь контроллера api, который создаёт пользователей.
Как этот контроллер должен получить объект Username?

class Controller {
function construct (Username $username) {
$this->Username = $username;
}

function add ($name) {
$this->Username->?
}
}


Получается никак не получить, имя ещё не известно, а зависимость уже нужно получить, а она требует имя, которое ещё не пришло.
И наследовать и как то расширить проект соответственно тоже нет возможности? Кроме как либо переписать исходники, либо проходить по проекту и менять Username на Username2?

Я думал куча new в проекте это уже пережитое прошлое.

_____________
Промокод предоставляет скидку на заказ домена и/или хостинга reg.ru
BFCC-3895-8804-9ED2
twin
Интересно... Я для чего ссылки даю в своих рассуждениях, не для Invis1ble же, он и так это всё знает. Почитал бы сначала, прежде чем такие вопросы задавать.

Но мне не лень, я расскажу.

Суть паттерна Value Object (VO) заключается в том, чтобы представить простые типы данных в виде объекта. Именно простые - строки, числа, даже можно boolean. И вести себя эти объекты должны как простые типы. Как ты отнаследуешься от строки или числа? Потому там и финал, чтобы наследование запретить.

Самый примитивный пример:
final class AnyString
{
private $value = 'Строка';

public function __toString(): string
{
return $this->value;
}
}
Получить значение этого объекта можно только обратившись к нему, как к строке. Что нам и требовалось. Можно вместо __toString() использовать геттер, который будет возвращать только нужный нам тип.
final class AnyInt
{
private $value = 10;

public function get(): int
{
return $this->value;
}
}
Можно быть уверенным, что это int и ни что иное.

Можно иметь несколько значений, это уже тип посложнее. Но основное требование к VO, это как раз иммутабельность. Тоесть состояние объекта ни при каких обстоятельствах не может быть изменено после создания.

В чем фишка.

1. В том, что в ООП всё должно быть объектом. И хотя это невозможно, но приверженцы чистого ООП (в частности DDD) стараются максимально это сделать. Так удобнее строить доменную модель и разговаривать на Ubiquitous Language

2. Удобно проверять не только абстрактный тип, но и более конкретный. Допустим если написать так:
   function construct (string $username) 
{
$this->Username = $username;
}
то аргументом может быть любая строка. А если так:

   function construct (Username $username) 
{
$this->Username = $username;
}
то не просто строка, а именно строка юзернейма.

3. Внутрь объекта можно поместить методы, которые относятся только к этому значению.
Что бы не распылять код по внешним плагинам. Допустим ту же валидацию. Или, как в примере с деньгами, можно сконвертировать валюту. Это как раз и предмет наших разночтений ситуации с Invis1ble. Он считает, что валидация должна производиться снаружи, хотя Фаулер для того и придумал этот паттерн, чтобы максимально собрать функционал, относящийся только к данному значению, вокруг этого значения, не размазывая код по сервисам.

Цитата (Arh @ 12.02.2018 - 13:37)
Получается никак не получить, имя ещё не известно, а зависимость уже нужно получить, а она требует имя, которое ещё не пришло.
Ничего она не требует. Посмотри внимательно на код. Максимально упрощаю:
final class Username
{
private $value;
private $checker = true;

public function withName(string $username): Username
{
if (empty($this->checker)) {
throw new \LogicException('Низя.');
}

$clone = clone $this;
$clone->value = $username;
unset($clone->checker);
return $clone;
}

public function __toString(): string
{
return $this->value;
}
}



Теперь можешь пробрасывать его куда хочешь.
class Controller {
function construct (Username $username) {
$this->Username = $username;
}

function add ($name) {
$newName = $this->Username->withName($name);
}
}

В $newName ты получишь новый объект со значением $name. Заполненный клон. А шаблон $this->Username останется нетронутым. И готовым к повторному использованию. Это, в совокупности с final и обеспечивает иммутабельность.

Invis1ble сделал еще жесче - вообще запретил создание новых объектов, сделав конструктор приватным. Но за это приходится "расплачиваться" фабрикой и рефлексией. :)

_____________
Если вам недостаточно собственных заблуждений, можно расширить их мнениями экспертов.

Нужно уважать мнение оппонета. Ведь заблуждаться - его святое право.

Настаивал, настаиваю и буду настаивать на своем. На кедровых орешках.

user posted image
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.