Объект-значение (Value Object)

Оглавление

1. Введение
2. Список методов
3. Примеры использования




Введение #
К оглавлению

При разработке приложений, особенно по принципу доменных моделей, часто возникает потребность в таком паттерне, как Объект-значение. Фреймворк предоставляет небольшой сервис для облегчения использования этого паттерна.

Задействуется он по общей схеме:
1
2
3

    $valueObject 
ABC::newService(ABC::VALUE_OBJECT);




Так мы получим пустой объект, в который можно поместить любое значение примитивного типа, и использовать это значение, как объект. Нужно отметить, что это всегда будет объект класса ValueObject. Если нужно изменить принадлежность, используйте прокси (смотри пример)
1
2
3
4
5
6
7
8
9
10
11
12

    
    $valueObject 
ABC::newService(ABC::VALUE_OBJECT); 
    
$string $valueObject->withValue('Это строка'); 
     
    
dbg($string);    /* object(Valueobject)#17 (1) { 
                          [$value:$Valueobject:private]=> 
                                 string(35)  'Это строка' }
                     */ 

    
echo $string// Это строка 




Сервис Value Object предоставляет следующие публичные методы #
К оглавлению


1 setValidator() Устанавливает внешний валидатор
2 setHandler() Устанавливает произвольный обработчик
3 withValue() Заполняет объект значением и возвращает новый объект
4 getValue() Получает значение из объекта
5 __toString() Возвращает строковое значение
6 __get() Возвращает значение, как свойство 'value'
7 equals() Сравнивает объекты-значения




setValidator() public method
К списку методов

Добавляет валидацию.

При настройке объекта, метод добавляет пользовательскую проверку значения в виде функции обратного вызова. После заполнения объекта значением метод недоступен.

Смотри еще setHandler().

public ValueObject::setValidator ( $validator )
$validator callable Функция обратного вызова, содержащая функционал валидации значения. Этот метод можно вызвать только до установки значения, иначе он сгенерирует ошибку и вернет false
return void




setHandler() public method
К списку методов

Добавляет произвольный функционал

При настройке объекта, метод добавляет произвольный функционал для обработки значения в виде функции обратного вызова. После заполнения объекта значением метод недоступен

Смотри еще setValidator().

public ValueObject::setHandler ( $callable )
$callable callable Функция обратного вызова, содержащая произвольный функционал обработки значения. Этот метод можно вызвать только до установки значения, иначе он сгенерирует ошибку и вернет false
return void




withValue() public method
К списку методов

Устанавливает значение в объект

Этот метод устанавливает в объект значение. Не допускаются типы object и boolean. Метод возвращает иммутабельный (неизменяемый) объект-значение.

public ValueObject::withValue ( $value )
$value mix Значение произвольного типа, кроме object и boolean
return object Иммутабельный клон объекта




getValue() public method
Получает значение из объекта

Этот метод возвращает значение любого типа из заполненного объекта

Этот метод

public ValueObject::getValue ( )
return mix Установленное значение




toString() public method
К списку методов

Получает строковое значение

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

public ValueObject::__toString( )
return string Установленное строковое значение




get() public method
К списку методов

Получает значение в виде свойства

Магический метод, возвращающий установленное значение любого валидного типа, как свойство 'value'

public ValueObject::__get( $name )
$name string Имя свойства (обязательно 'value')
return mix Установленное значение




equals() public method
К списку методов

Сравнивает объекты

Метод сравнивает значение текущего объекта со значением объекта, переданного аргументом

public ValueObject::equals( $object )
$object object Объект для сравнения значений
return boolean true если объект-значения равны, и false в противном случае.




Примеры использования #
К оглавлению

Установка валидатора и произвольных обработчиков. #
Приоритет у валидации. Обработчики будут выполняться в том порядке, в каком они установлены.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

    
// Получаем пустой объект сервиса     
    
$valueObject ABC::newService(ABC::VALUE_OBJECT); 
     
// Устанавливаем валидатор. Колбэк должен вернуть булев тип  
    
$valueObject->setValidator(function ($value) { 
        return 
is_string($value); 
    }); 
     
// Устанавливаем обработчик.  
    
$valueObject->setHandler(function ($value) { 
        return 
mb_strtoupper($value'utf-8'); 
    });  
// Можно несколько, выполняться будут по очереди 
    
$valueObject->setHandler(function ($value) { 
        return 
'Привет, '$value .'!'
    });  
// Заполняем объект и получаем новый объект-значение 
    
$world $valueObject->withValue('мир'); 
// Это будет другой объект 
    
$john  $valueObject->withValue('john'); 

// Получаем значение, как сторку     
    
echo $world
    echo 
'<br>'
// Получаем значение методом getValue() 
    
echo $john->getValue(); 
    echo 
'<br>';
// Получаем значение в виде свойства "value" 
    
echo $john->value
    echo 
'<br>';
    
/* Результат: 

Привет, МИР! 
Привет, JOHN! 
Привет, JOHN! 

*/ 




Прокси #
Сервис всегда возвращает объекты класса ValueObject. Это не всегда удобно при передаче их параметрами, когда нужна интроспекция. Для того, чтобы изменить принадлежность объекта классу, можно использовать проксирование:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

    
// Используем прокси для смены принадлежности объекта
class UserName
{
    public function 
__construct(string $name)
    {
        
$valueObject ABC::newService(ABC::VALUE_OBJECT);
        
$this->username $valueObject->withValue($name);
    }
    
    public function 
__call($method$parameters)
    {
        return 
call_user_func_array([$this->username$method], $parameters);
    }  
}
    
// Метод этого класса примет только объект UserName    
class User
{
    public function 
displayName(UserName $username)
    {
        echo 
$username->getValue();
    }  
}        
    
    
    
$username = new UserName('John');    
    (new 
User)->displayName($username); // 'John'




Магические методы #
Стоит отметить, что при проксировании не работают магические методы. Если это необходимо, нужно продублировать их в прокси-классе:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

    
// Используем прокси для смены принадлежности объекта
class UserName
{
    public function 
__construct(string $name)
    {
        
$valueObject ABC::newService(ABC::VALUE_OBJECT);
        
$this->username $valueObject->withValue($name);
    }
    
    
// Дублируем магию
    
public function __toString()
    {
        return (string)
$this->username;
    }
}
    
// Метод этого класса примет только объект UserName    
class User
{
    public function 
displayName(UserName $username)
    { 
// Теперь можно обратиться, как к строке
        
echo $username;
    } 
}        

    
$username = new UserName('John');    
    (new 
User)->displayName($username); // 'John'




Сравнение #
Иногда нужно сравнить два объекта на идентичность. Но простое сравнение тут не годится, нельзя сравнивать два объекта, даже если они идентичны. Для такого сравнения используется метод equals()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

    
// Получаем пустой объект сервиса     
    
$valueObject ABC::newService(ABC::VALUE_OBJECT); 

// Заполняем объект и получаем новый объект-значение 
    
$john $valueObject->withValue('john'); 
// Это будет другой объект с таким же значением 
    
$otherJohn $valueObject->withValue('john'); 
     
// Если сравнивать влоб, то ничего не выйдет:     
    
echo ($john === $otherJohn) ? 'Равны' 'Нет'// Нет 
// А если так, все хорошо     
    
echo $john->equals($otherJohn) ? 'Равны' 'Нет'// Равны