今天分享一些 DDD 抽象類別的實作。
這邊使用 __set
與 __get
模式方法來時做出一個只能 new 而不能改值得物件
不過實際的類別要是使用 public setters 那就另當別論了。
以 PHP 來說,這部分只能依靠團隊共識跟 Code review 來補足了。
use ArrayAccess;
abstract class ValueObject implements ArrayAccess
{
protected $props = [];
const SETTER_ACCESS_MESSAGE = 'The value of the ValueObject cannot be modified';
public function __construct(array $props)
{
$this->props = $props;
}
public function __set($key, $value)
{
throw new Exception(static::SETTER_ACCESS_MESSAGE);
}
public function __get($key)
{
return $this->props[$key] ?? null;
}
public function offsetSet($offset, $value)
{
throw new Exception(static::SETTER_ACCESS_MESSAGE);
}
public function offsetExists($offset)
{
return isset($this->props[$offset]);
}
public function offsetUnset($offset)
{
throw new Exception(static::SETTER_ACCESS_MESSAGE);
}
public function offsetGet($offset)
{
return $this->props[$offset] ?? null;
}
public function toArray()
{
return $this->props;
}
}
class Color extends ValueObject
{
// ...
}
Entity 繼承了 ValueObject 並把 setters 打開
abstract class Entity extends ValueObject
{
public function __set($key, $value)
{
$this->props[$key] = $value;
return $value;
}
public function offsetSet($offset, $value)
{
$this->props[$offset] = $value;
}
public function offsetUnset($offset)
{
unset($this->props[$offset]);
}
}
Entity 的子類別可以依照所需再次複寫這些方法,例如:
<?php
class User
{
public function __construct($props)
{
$this->props = $props;
}
public function __set($key, $value)
{
if ($key === 'email') {
$this->setEmail($value);
} else {
$this->props[$key] = $value;
}
return $value;
}
public function setEmail($value)
{
var_dump('Check email format ...');
$this->props['email'] = $value;
return $this;
}
public function toArray()
{
return $this->props;
}
}
$user = new User([
'name' => 'Petter'
]);
$user->email = 'example@mail.com';
var_dump($user->toArray());
// output:
// string(22) "Check email format ..."
// array(2) {
// ["props"]=>
// array(1) {
// ["name"]=>
// string(6) "Petter"
// }
// ["email"]=>
// string(16) "example@mail.com"
// }
這時候的 setters 是否要設為 public,就看自己的開發團隊共識。
Aggregate 實際上是 Entity 的子類因此沒有抽象類,延續上一個案例的 User 類別
它的 Aggregate 可能像是這樣:
use User;
class UserAggregate extends User
{
public function __set($key, $value)
{
if ($key === 'group') {
$this->setGroup($value);
} else {
parent::__set($key, $value);
}
}
public function setGroup($group)
{
var_dump('Check something ...');
$this->props['group'] = $group;
return $this;
}
}
$user = new UserAggregate([
'name' => 'Petter'
]);
$user->email = 'example@mail.com';
$user->group = 'MyGroup';
var_dump($user->toArray());
// output:
// string(22) "Check email format ..."
// string(19) "Check something ..."
// array(3) {
// ["props"]=>
// array(1) {
// ["name"]=>
// string(6) "Petter"
// }
// ["email"]=>
// string(16) "example@mail.com"
// ["group"]=>
// string(7) "MyGroup"
// }
基本上就是變成丟進建構子的 $props 屬性變多了,然後對增加的那些屬性新增 getters/setters。