Php 变异对象以避免副作用的正确方法是什么?
我有下一个对象系统(简单示例): 免责声明:Php 变异对象以避免副作用的正确方法是什么?,php,unit-testing,architecture,immutability,Php,Unit Testing,Architecture,Immutability,我有下一个对象系统(简单示例): 免责声明:Grid类是一个遗留的ActiveRecord模型,独立的单元测试无法覆盖它,因为它会写入数据库并更改系统中的一些其他数据。所以我只对State类感兴趣 我需要一个用于状态的mutator类。它一定很容易测试。看起来是这样的: class StateMutator { public function mutate(State $state, array $changes):?State { // ...
Grid
类是一个遗留的ActiveRecord模型,独立的单元测试无法覆盖它,因为它会写入数据库并更改系统中的一些其他数据。所以我只对State
类感兴趣
我需要一个用于状态
的mutator类。它一定很容易测试。看起来是这样的:
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在获取的对象中做了一些更改,并在此之后返回null
,那么调用代码将认为状态没有更改-对其进行一些其他更改并将其保存到数据库中。但是由于PHP通过引用传递对象,所以在state对象中所做的更改也将保存到数据库中。这是个问题
我应该怎么做来避免这个问题
我有两种方法来解决这个问题,但它们都有很大的问题
有人知道吗?我通常会避免对象进入无效状态。您的
状态
对象应该有方法来改变其状态或返回具有新状态的克隆。这些方法验证输入,并仅在生成的状态有效时才变异/返回克隆。无需跟踪更改和回滚。我认为这个“mutator”不应该修改$state
,如果它的任务只是返回更新的状态。您是否有理由希望在mutate()
中修改$state
?是的,我同意您的看法。所以我问我该怎么做?为了返回一个新的状态,突变子应该克隆一个源状态。但是它需要大量的内存。我搜索了另一种方法来实现它。我不明白为什么需要mutate()
来返回任何东西。它不能仅仅是无效的吗?你没有使用它的返回值,只是把它再次赋给$grid->state
,如果对象已经变异了,这是没有用的,对吧?克隆似乎是正确的方法。我还认为状态
应该是不可变的,以保证一个给定的状态永远不会被改变。记忆有什么大不了的吗?你说的是“因为PHP通过引用传递对象”,但如果不是这样,克隆它们正是它要做的事情。还有一个想法——你能创建一个StateChangeHistory对象来跟踪状态发生了什么,这样你就可以恢复所做的任何更改吗?
/** @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();