创建无法在PHP中更改的对象是一个好主意吗?
例如,具有setter方法的日期对象,但它们将始终返回对象的新实例(具有修改日期)。
这些对象是否会让使用该类的其他人感到困惑,因为在PHP中,您通常希望该对象发生变化?
实施例
$obj = new Object(2);
$x = $obj->add(5); // 7
$y = $obj->add(2); // 4
答案 0 :(得分:22)
不可变对象没有setter方法。周期。
每个人都希望setXyz()
方法具有void返回类型(或者在松散类型的语言中不返回任何内容)。如果你确实为你的不可变对象添加了setter方法,那么它会让人感到困惑并导致丑陋的错误。
答案 1 :(得分:9)
在我看来,对象对于值对象应该是不可变的。除此之外,除非您在整个应用程序中共享对象,否则它没有太大的好处。
这里有一些错误的答案,不可变对象可以有setter 。这是PHP中不可变对象的一些实现。
示例#1。
class ImmutableValueObject
{
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function getVal2()
{
return $this->val2;
}
}
正如您所见,一旦实例化,您就无法更改任何值。
示例2:setters
:
class ImmutableValueObject
{
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function withVal1($val1)
{
$copy = clone $this;
$copy->val1 = $val1;
return $copy; // here's the trick: you return a new instance!
}
public function getVal2()
{
return $this->val2;
}
public function withVal2($val2)
{
$copy = clone $this;
$copy->val2 = $val2;
return $copy;
}
}
可以实现多种实现,这绝不是排他性列表。请记住,使用Reflection总有一种方法可以在PHP中解决这个问题,所以最终你的不变性就在你的脑海中!
将不可变对象作为最终对象通常也是一种好习惯。
编辑:
答案 2 :(得分:6)
不可变对象在初始创建后不能更改,因此使用setter方法没有意义,因为它违背了基本原则。
您可以通过操纵类成员可见性并覆盖magic __set()方法来实现一些变通方法来模拟PHP中的不变性,但不能保证不可变,因为不变性不是该语言的一个特性。我相信有人曾经写过一个扩展来在PHP中提供一个不可变的值类型,所以你可以谷歌那样。
答案 3 :(得分:0)
我做了一些避免使用反射来简化不变性实现的特征:https://github.com/jclaveau/php-immutable-trait
很明显,由于它不是语言功能,因此不会通过魔术弹mutation突变,但是可以减轻必须先克隆当前实例才能应用的突变体的代码。将其应用于Massimiliano的示例中
class ImmutableValueObject
{
use JClaveau\Traits\Immutable;
private $val1;
private $val2;
public function __construct($val1, $val2)
{
$this->val1 = $val1;
$this->val2 = $val2;
}
public function getVal1()
{
return $this->val1;
}
public function withVal1($val1)
{
// Just add these lines at the really beginning of methods supporting
// immutability ("setters" mostly)
if ($this->callOnCloneIfImmutable($result))
return $result;
// Write your method's body as if you weren't in an Immutable class
$this->val1 = $val1;
return $this;
}
public function getVal2()
{
return $this->val2;
}
public function withVal2($val2)
{
if ($this->callOnCloneIfImmutable($result))
return $result;
$this->val2 = $val2;
return $this;
}
}
希望它可以帮助这里的某些人!
PS:真的欢迎您提出有关此小功能的建议:):https://github.com/jclaveau/php-immutable-trait/issues
答案 4 :(得分:0)
从一个不可变的对象,您可以获取其值,但无法修改它们。在这里,您可以看到一个不可变类的示例:
<?php
declare(strict_types=1);
final class Immutable
{
/** @var string */
private $value;
public static function withValue(string $value): self
{
return new self($value);
}
public function __construct(string $value)
{
$this->value = $value;
}
public function value(): string
{
return $this->value;
}
}
// Example of usage:
$immutable = Immutable::withValue("my value");
$immutable->value();
答案 5 :(得分:0)
如果您想在类和对象上使用setter,这是完全可以的,我们会一直设置对象数据,因此我们会一直这样做。只是根本不称它为一成不变。
开发人员世界中的许多事情都是主观的-我们的方法,方法论等-但“不变”是一个非常扎实的定义:
“不可变”:
-随时间不变或无法更改。
如果您想要一个不可变的对象,则意味着在实例化之后不能对其进行更改。这对于诸如DB数据库中需要在整个周期内保持不变的数据之类的事情有好处。
如果您需要在实例化后调用该对象并设置或更改其上的数据,则这不是一个不变的对象。
您会从汽车上拆下两个车轮并将其称为摩托车吗?
关于“不可变”类的方法有一些讨论,这些方法不带单词“ set”,但是这并不能阻止它们作为设置数据的方法的功能。您可以将其称为thisDoesNotSetAnything(int $id)
,并允许在其中更改对象的数据进行传递。这将是一个setter,因此该对象是可变的。
答案 6 :(得分:0)
使对象不可变非常容易。这是一种优雅而方便的方法。
您需要做的就是使用特定的 __get()
和 __set()
魔术方法创建基本抽象类,并在子对象中扩展此基类。
如果您使用值对象(例如用于 DDD),这非常适用。
这是基类:
abstract class BaseValueObject
{
public function __get(string $propertyName)
{
return $this->$propertyName;
}
public function __set(string $propertyName, $value): void
{
throw new \Exception("Cannot set property {$propertyName}. The object is immutable.");
}
}
现在是一个子对象(好吧,就是它的类)。
class CategoryVO extends BaseValueObject
{
public $id;
public $name;
public function __construct(array $data)
{
$this->id = $data['id'];
$this->name = $data['name'];
}
}
就是这样。
它会在尝试设置某个值时引发异常。基本上它是不可变的。
然而,它会方便地将其所有属性公开为只读(对于某种序列化等),这与我们将它们设为私有不同。
最后不能错误地实例化BaseValueObject
,因为它是一个抽象类。从各个角度来看,都是不错的优雅解决方案。
答案 7 :(得分:0)
希望不变性功能快点推出https://wiki.php.net/rfc/immutability