改变对象以避免副作用的正确方法是什么?
What is the right way to mutate objects to avoid side effects?
我有下一个对象系统(简单示例):
class Grid
{
public State $state;
// Other fields
public function __construct(State $state)
{
$this->state = $state;
}
// ...
}
class State
{
public bool $isCompleted = false;
public ?User $judge;
}
class User
{
}
免责声明:Grid
class 是遗留的 ActiveRecord 模型,它不能被独立的单元测试覆盖,因为它写入数据库并更改系统中的一些其他数据。所以我只对 State
class.
感兴趣
我需要 State
的突变体 class。它必须易于测试。它看起来像这样:
class StateMutator
{
public function mutate(State $state, array $changes):?State
{
// ...
$state->isCompleted = true;
// ...
if(!$someCondition){
return null;
}
// ...
return $state;
}
}
并且是这样使用的:
/** @var Grid $grid */
/** @var array $changes */
$newState = (new StateMutator())->mutate($grid->state, $changes);
if($newState !== null){
$grid->state = $newState;
}
// Some other changes in $grid
$grid->saveChanges();
看起来不错。但是有些事情让我感到困惑。如果 mutator 在获得的对象中做了一些更改,然后 returns null
,那么调用代码会认为 State 没有改变 - 在其中进行一些其他更改并将其保存到数据库中。但是因为 PHP 通过引用传递对象,所以 mutator 在状态对象中所做的更改也将保存到数据库中。这是一个问题。
我应该怎么做才能避免这个问题?
我有两种方法可以解决这个问题,但是它们都有很大的问题。
- 如果修改器不能在它内部的任何地方改变状态对象,它应该恢复它已经完成的改变。但在某些情况下很难做到甚至不可能。
- 变异器应该克隆状态,变异它的副本并return它。但在这种情况下,该方法将需要更多的内存(状态中可以有超过 1000 个对象 属性)。
有人知道吗?
我通常会避免对象进入无效状态。您的 State
对象应该具有改变其状态或 return 具有新状态的克隆的方法。这些方法仅在结果状态有效时才验证输入和 mutate/return 克隆。无需跟踪更改和回滚。
我有下一个对象系统(简单示例):
class Grid
{
public State $state;
// Other fields
public function __construct(State $state)
{
$this->state = $state;
}
// ...
}
class State
{
public bool $isCompleted = false;
public ?User $judge;
}
class User
{
}
免责声明:Grid
class 是遗留的 ActiveRecord 模型,它不能被独立的单元测试覆盖,因为它写入数据库并更改系统中的一些其他数据。所以我只对 State
class.
我需要 State
的突变体 class。它必须易于测试。它看起来像这样:
class StateMutator
{
public function mutate(State $state, array $changes):?State
{
// ...
$state->isCompleted = true;
// ...
if(!$someCondition){
return null;
}
// ...
return $state;
}
}
并且是这样使用的:
/** @var Grid $grid */
/** @var array $changes */
$newState = (new StateMutator())->mutate($grid->state, $changes);
if($newState !== null){
$grid->state = $newState;
}
// Some other changes in $grid
$grid->saveChanges();
看起来不错。但是有些事情让我感到困惑。如果 mutator 在获得的对象中做了一些更改,然后 returns null
,那么调用代码会认为 State 没有改变 - 在其中进行一些其他更改并将其保存到数据库中。但是因为 PHP 通过引用传递对象,所以 mutator 在状态对象中所做的更改也将保存到数据库中。这是一个问题。
我应该怎么做才能避免这个问题?
我有两种方法可以解决这个问题,但是它们都有很大的问题。
- 如果修改器不能在它内部的任何地方改变状态对象,它应该恢复它已经完成的改变。但在某些情况下很难做到甚至不可能。
- 变异器应该克隆状态,变异它的副本并return它。但在这种情况下,该方法将需要更多的内存(状态中可以有超过 1000 个对象 属性)。
有人知道吗?
我通常会避免对象进入无效状态。您的 State
对象应该具有改变其状态或 return 具有新状态的克隆的方法。这些方法仅在结果状态有效时才验证输入和 mutate/return 克隆。无需跟踪更改和回滚。